diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 80d9493daca427..2f675f6984cd4d 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -2175,6 +2175,13 @@ bool Compiler::StructPromotionHelper::ShouldPromoteStructVar(unsigned lclNum) lclNum); shouldPromote = false; } +#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) + else if (varDsc->lvIsSplit) + { + JITDUMP("Not promoting multireg struct local V%02u, because it is splitted.\n", lclNum); + shouldPromote = false; + } +#endif // TARGET_LOONGARCH64 || TARGET_RISCV64 } else #endif // !FEATURE_MULTIREG_STRUCT_PROMOTE diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs index f056970ef5db51..566d5ac46f5625 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpRequestMessage.cs @@ -147,7 +147,14 @@ public override string ToString() sb.Append(_method); sb.Append(", RequestUri: '"); - sb.Append(_requestUri == null ? "" : _requestUri.ToString()); + if (_requestUri is null) + { + sb.Append(""); + } + else + { + sb.Append($"{_requestUri}"); + } sb.Append("', Version: "); sb.Append(_version); diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs index af18e4d388a7de..49b2d19b83d772 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/DataContract.cs @@ -1673,17 +1673,27 @@ private static void CheckExplicitDataContractNamespaceUri(string dataContractNs, { string trimmedNs = dataContractNs.Trim(); // Code similar to XmlConvert.ToUri (string.Empty is a valid uri but not " ") - if (trimmedNs.Length == 0 || trimmedNs.IndexOf("##", StringComparison.Ordinal) != -1) + if (trimmedNs.Length == 0 || trimmedNs.Contains("##", StringComparison.Ordinal)) + { ThrowInvalidDataContractException(SR.Format(SR.DataContractNamespaceIsNotValid, dataContractNs), type); + } + dataContractNs = trimmedNs; } if (Uri.TryCreate(dataContractNs, UriKind.RelativeOrAbsolute, out Uri? uri)) { - if (uri.ToString() == Globals.SerializationNamespace) + Span formatted = stackalloc char[Globals.SerializationNamespace.Length]; + if (uri.TryFormat(formatted, out int charsWritten) && + charsWritten == Globals.SerializationNamespace.Length && + formatted.SequenceEqual(Globals.SerializationNamespace)) + { ThrowInvalidDataContractException(SR.Format(SR.DataContractNamespaceReserved, Globals.SerializationNamespace), type); + } } else + { ThrowInvalidDataContractException(SR.Format(SR.DataContractNamespaceIsNotValid, dataContractNs), type); + } } internal static string GetClrTypeFullName(Type type) diff --git a/src/libraries/System.Private.Uri/src/System/Uri.cs b/src/libraries/System.Private.Uri/src/System/Uri.cs index df8060e1943798..4b12cc187f864a 100644 --- a/src/libraries/System.Private.Uri/src/System/Uri.cs +++ b/src/libraries/System.Private.Uri/src/System/Uri.cs @@ -17,7 +17,7 @@ namespace System { [Serializable] [System.Runtime.CompilerServices.TypeForwardedFrom("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - public partial class Uri : ISerializable + public partial class Uri : ISpanFormattable, ISerializable { public static readonly string UriSchemeFile = UriParser.FileUri.SchemeName; public static readonly string UriSchemeFtp = UriParser.FtpUri.SchemeName; @@ -46,7 +46,7 @@ public partial class Uri : ISerializable // or idn is on and we have unicode host or idn host // In that case, this string is normalized, stripped of bidi chars, and validated // with char limits - private string _string = null!; // initialized early in ctor via a helper + private string _string; // untouched user string if string has unicode with iri on or unicode/idn host with idn on private string _originalUnicodeString = null!; // initialized in ctor via helper @@ -319,6 +319,7 @@ private static bool StaticInFact(Flags allFlags, Flags checkFlags) return (allFlags & checkFlags) != 0; } + [MemberNotNull(nameof(_info))] private UriInfo EnsureUriInfo() { Flags cF = _flags; @@ -338,6 +339,7 @@ private void EnsureParseRemaining() } } + [MemberNotNull(nameof(_info))] private void EnsureHostString(bool allowDnsOptimization) { UriInfo info = EnsureUriInfo(); @@ -497,6 +499,7 @@ protected void GetObjectData(SerializationInfo serializationInfo, StreamingConte } } + [MemberNotNull(nameof(_string))] private void CreateUri(Uri baseUri, string? relativeUri, bool dontEscape) { DebugAssertInCtor(); @@ -1173,7 +1176,7 @@ public string IdnHost { EnsureHostString(false); - string host = _info!.Host!; + string host = _info.Host!; Flags hostType = HostType; if (hostType == Flags.DnsHostType) @@ -1538,8 +1541,6 @@ public override int GetHashCode() // // ToString // - // The better implementation would be just - // private const UriFormat V1ToStringUnescape = (UriFormat)0x7FFF; public override string ToString() @@ -1550,16 +1551,93 @@ public override string ToString() } EnsureUriInfo(); - if (_info.String is null) + return _info.String ??= + _syntax.IsSimple ? + GetComponentsHelper(UriComponents.AbsoluteUri, V1ToStringUnescape) : + GetParts(UriComponents.AbsoluteUri, UriFormat.SafeUnescaped); + } + + /// + /// Attempts to format a canonical string representation for the instance into the specified span. + /// + /// The span into which to write this instance's value formatted as a span of characters. + /// When this method returns, contains the number of characters that were written in . + /// if the formatting was successful; otherwise, . + public bool TryFormat(Span destination, out int charsWritten) + { + ReadOnlySpan result; + + if (_syntax is null) { - if (_syntax.IsSimple) - _info.String = GetComponentsHelper(UriComponents.AbsoluteUri, V1ToStringUnescape); + result = _string; + } + else + { + EnsureUriInfo(); + if (_info.String is not null) + { + result = _info.String; + } else - _info.String = GetParts(UriComponents.AbsoluteUri, UriFormat.SafeUnescaped); + { + UriFormat uriFormat = V1ToStringUnescape; + if (!_syntax.IsSimple) + { + if (IsNotAbsoluteUri) + { + throw new InvalidOperationException(SR.net_uri_NotAbsolute); + } + + if (UserDrivenParsing) + { + throw new InvalidOperationException(SR.Format(SR.net_uri_UserDrivenParsing, GetType())); + } + + if (DisablePathAndQueryCanonicalization) + { + throw new InvalidOperationException(SR.net_uri_GetComponentsCalledWhenCanonicalizationDisabled); + } + + uriFormat = UriFormat.SafeUnescaped; + } + + EnsureParseRemaining(); + EnsureHostString(allowDnsOptimization: true); + + ushort nonCanonical = (ushort)((ushort)_flags & (ushort)Flags.CannotDisplayCanonical); + if (((_flags & (Flags.ShouldBeCompressed | Flags.FirstSlashAbsent | Flags.BackslashInPath)) != 0) || + (IsDosPath && _string[_info.Offset.Path + SecuredPathIndex - 1] == '|')) // A rare case of c|\ + { + nonCanonical |= (ushort)Flags.PathNotCanonical; + } + + if (((ushort)UriComponents.AbsoluteUri & nonCanonical) != 0) + { + return TryRecreateParts(destination, out charsWritten, UriComponents.AbsoluteUri, nonCanonical, uriFormat); + } + + result = _string.AsSpan(_info.Offset.Scheme, _info.Offset.End - _info.Offset.Scheme); + } + } + + if (result.TryCopyTo(destination)) + { + charsWritten = result.Length; + return true; } - return _info.String; + + charsWritten = 0; + return false; } + /// + bool ISpanFormattable.TryFormat(Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) => + TryFormat(destination, out charsWritten); + + /// + string IFormattable.ToString(string? format, IFormatProvider? formatProvider) => + ToString(); + public static bool operator ==(Uri? uri1, Uri? uri2) { if (ReferenceEquals(uri1, uri2)) @@ -1664,7 +1742,7 @@ public override bool Equals([NotNullWhen(true)] object? comparand) EnsureUriInfo(); obj.EnsureUriInfo(); - if (!UserDrivenParsing && !obj.UserDrivenParsing && Syntax!.IsSimple && obj.Syntax!.IsSimple) + if (!UserDrivenParsing && !obj.UserDrivenParsing && Syntax!.IsSimple && obj.Syntax.IsSimple) { // Optimization of canonical DNS names by avoiding host string creation. // Note there could be explicit ports specified that would invalidate path offsets @@ -2580,7 +2658,7 @@ private string GetEscapedParts(UriComponents uriParts) } } - return ReCreateParts(uriParts, nonCanonical, UriFormat.UriEscaped); + return RecreateParts(uriParts, nonCanonical, UriFormat.UriEscaped); } private string GetUnescapedParts(UriComponents uriParts, UriFormat formatAs) @@ -2615,12 +2693,12 @@ private string GetUnescapedParts(UriComponents uriParts, UriFormat formatAs) } } - return ReCreateParts(uriParts, nonCanonical, formatAs); + return RecreateParts(uriParts, nonCanonical, formatAs); } - private string ReCreateParts(UriComponents parts, ushort nonCanonical, UriFormat formatAs) + private string RecreateParts(UriComponents parts, ushort nonCanonical, UriFormat formatAs) { - EnsureHostString(false); + EnsureHostString(allowDnsOptimization: false); string str = _string; @@ -2628,6 +2706,33 @@ private string ReCreateParts(UriComponents parts, ushort nonCanonical, UriFormat ? new ValueStringBuilder(stackalloc char[StackallocThreshold]) : new ValueStringBuilder(str.Length); + scoped ReadOnlySpan result = RecreateParts(ref dest, str, parts, nonCanonical, formatAs); + + string s = result.ToString(); + dest.Dispose(); + return s; + } + + private bool TryRecreateParts(scoped Span span, out int charsWritten, UriComponents parts, ushort nonCanonical, UriFormat formatAs) + { + EnsureHostString(allowDnsOptimization: false); + + string str = _string; + + var dest = str.Length <= StackallocThreshold + ? new ValueStringBuilder(stackalloc char[StackallocThreshold]) + : new ValueStringBuilder(str.Length); + + scoped ReadOnlySpan result = RecreateParts(ref dest, str, parts, nonCanonical, formatAs); + + bool copied = result.TryCopyTo(span); + charsWritten = copied ? result.Length : 0; + dest.Dispose(); + return copied; + } + + private ReadOnlySpan RecreateParts(scoped ref ValueStringBuilder dest, string str, UriComponents parts, ushort nonCanonical, UriFormat formatAs) + { //Scheme and slashes if ((parts & UriComponents.Scheme) != 0) { @@ -2778,9 +2883,7 @@ private string ReCreateParts(UriComponents parts, ushort nonCanonical, UriFormat offset = 0; } - string result = dest.AsSpan(offset).ToString(); - dest.Dispose(); - return result; + return dest.AsSpan(offset); } } @@ -2860,9 +2963,9 @@ private string ReCreateParts(UriComponents parts, ushort nonCanonical, UriFormat ref dest, '#', c_DummyChar, c_DummyChar, mode, _syntax, isQuery: false); } - AfterFragment: - return dest.ToString(); + AfterFragment: + return dest.AsSpan(); } // diff --git a/src/libraries/System.Private.Uri/src/System/UriExt.cs b/src/libraries/System.Private.Uri/src/System/UriExt.cs index d38ac6e1a87753..6eefa363134135 100644 --- a/src/libraries/System.Private.Uri/src/System/UriExt.cs +++ b/src/libraries/System.Private.Uri/src/System/UriExt.cs @@ -13,6 +13,7 @@ public partial class Uri // // All public ctors go through here // + [MemberNotNull(nameof(_string))] private void CreateThis(string? uri, bool dontEscape, UriKind uriKind, in UriCreationOptions creationOptions = default) { DebugAssertInCtor(); @@ -910,6 +911,7 @@ internal bool IsBaseOfHelper(Uri uriLink) // // Only a ctor time call // + [MemberNotNull(nameof(_string))] private void CreateThisFromUri(Uri otherUri) { DebugAssertInCtor(); diff --git a/src/libraries/System.Private.Uri/src/System/UriHelper.cs b/src/libraries/System.Private.Uri/src/System/UriHelper.cs index 1c8081b6dce60a..3fa5b49893fd3d 100644 --- a/src/libraries/System.Private.Uri/src/System/UriHelper.cs +++ b/src/libraries/System.Private.Uri/src/System/UriHelper.cs @@ -269,7 +269,7 @@ internal static unsafe void UnescapeString(string input, int start, int end, ref UnescapeString(pStr, start, end, ref dest, rsvd1, rsvd2, rsvd3, unescapeMode, syntax, isQuery); } } - internal static unsafe void UnescapeString(ReadOnlySpan input, ref ValueStringBuilder dest, + internal static unsafe void UnescapeString(scoped ReadOnlySpan input, scoped ref ValueStringBuilder dest, char rsvd1, char rsvd2, char rsvd3, UnescapeMode unescapeMode, UriParser? syntax, bool isQuery) { fixed (char* pStr = &MemoryMarshal.GetReference(input)) diff --git a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriTests.cs b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriTests.cs index ca302b4be1c1c8..5f3d986da399fe 100644 --- a/src/libraries/System.Private.Uri/tests/FunctionalTests/UriTests.cs +++ b/src/libraries/System.Private.Uri/tests/FunctionalTests/UriTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Globalization; using System.Threading; using System.Threading.Tasks; using Xunit; @@ -862,5 +863,61 @@ public static void ZeroPortIsParsedForBothKnownAndUnknownSchemes(string uriStrin Assert.Equal(isDefaultPort, uri.IsDefaultPort); Assert.Equal(uriString + "/", uri.ToString()); } + + public static IEnumerable ToStringTest_MemberData() + { + // Return funcs rather than Uri instances directly so that: + // a) We can test each method without it being impacted by implicit caching of a previous method's results + // b) xunit's implicit formatting of arguments doesn't similarly disturb the results + + yield return new object[] { () => new Uri("http://test"), "http://test/" }; + yield return new object[] { () => new Uri(" http://test "), "http://test/" }; + yield return new object[] { () => new Uri("/test", UriKind.Relative), "/test" }; + yield return new object[] { () => new Uri("test", UriKind.Relative), "test" }; + yield return new object[] { () => new Uri("http://foo/bar/baz#frag"), "http://foo/bar/baz#frag" }; + yield return new object[] { () => new Uri(new Uri(@"http://www.contoso.com/"), "catalog/shownew.htm?date=today"), "http://www.contoso.com/catalog/shownew.htm?date=today" }; + yield return new object[] { () => new Uri("http://test/a/b/c/d/../../e/f"), "http://test/a/b/e/f" }; + yield return new object[] { () => { var uri = new Uri("http://test/a/b/c/d/../../e/f"); uri.ToString(); return uri; }, "http://test/a/b/e/f" }; + } + + [Theory] + [MemberData(nameof(ToStringTest_MemberData))] + public static void ToStringTest(Func func, string expected) + { + // object.ToString + Assert.Equal(expected, func().ToString()); + + // IFormattable.ToString + Assert.Equal(expected, ((IFormattable)func()).ToString("asdfasdf", new CultureInfo("fr-FR"))); + + // TryFormat - Big enough destination + foreach (int length in new[] { expected.Length, expected.Length + 1 }) + { + // TryFormat + char[] formatted = new char[length]; + Assert.True(func().TryFormat(formatted, out int charsWritten)); + AssertExtensions.SequenceEqual(expected, (ReadOnlySpan)formatted.AsSpan(0, charsWritten)); + Assert.Equal(expected.Length, charsWritten); + + // ISpanFormattable.TryFormat + Array.Clear(formatted); + Assert.True(((ISpanFormattable)func()).TryFormat(formatted, out charsWritten, "asdfasdf", new CultureInfo("fr-FR"))); + AssertExtensions.SequenceEqual(expected, (ReadOnlySpan)formatted.AsSpan(0, charsWritten)); + Assert.Equal(expected.Length, charsWritten); + } + + // TryFormat - Too small destination + { + char[] formatted = new char[expected.Length - 1]; + + // TryFormat + Assert.False(func().TryFormat(formatted, out int charsWritten)); + Assert.Equal(0, charsWritten); + + // ISpanFormattable.TryFormat + Assert.False(((ISpanFormattable)func()).TryFormat(formatted, out charsWritten, default, null)); + Assert.Equal(0, charsWritten); + } + } } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index d0b807ee15d0aa..e5421cf35e93a5 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -15544,7 +15544,7 @@ public partial class NewsStyleUriParser : System.UriParser { public NewsStyleUriParser() { } } - public partial class Uri : System.Runtime.Serialization.ISerializable + public partial class Uri : System.ISpanFormattable, System.Runtime.Serialization.ISerializable { public static readonly string SchemeDelimiter; public static readonly string UriSchemeFile; @@ -15639,10 +15639,13 @@ protected void GetObjectData(System.Runtime.Serialization.SerializationInfo seri protected virtual void Parse() { } void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) { } public override string ToString() { throw null; } + string System.IFormattable.ToString(string? format, System.IFormatProvider? formatProvider) { throw null; } public static bool TryCreate([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true), System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("Uri")] string? uriString, in System.UriCreationOptions creationOptions, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Uri? result) { throw null; } public static bool TryCreate([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true), System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("Uri", "uriKind")] string? uriString, System.UriKind uriKind, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Uri? result) { throw null; } public static bool TryCreate(System.Uri? baseUri, string? relativeUri, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Uri? result) { throw null; } public static bool TryCreate(System.Uri? baseUri, System.Uri? relativeUri, [System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] out System.Uri? result) { throw null; } + public bool TryFormat(System.Span destination, out int charsWritten) { throw null; } + bool System.ISpanFormattable.TryFormat(System.Span destination, out int charsWritten, System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; } [System.ObsoleteAttribute("Uri.Unescape has been deprecated. Use GetComponents() or Uri.UnescapeDataString() to unescape a Uri component or a string.")] protected virtual string Unescape(string path) { throw null; } public static string UnescapeDataString(string stringToUnescape) { throw null; }