Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/ - #126287

Merged
EgorBo merged 22 commits into
mainfrom
common-pollyfills
Apr 1, 2026
Merged

Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/#126287
EgorBo merged 22 commits into
mainfrom
common-pollyfills

Conversation

@EgorBo

@EgorBoEgorBo commented Mar 30, 2026

Copy link
Copy Markdown
Member

This allows us to pretend new APIs exist for .NET Standard 2.0/NETFX/etc targets and avoid #if NET else mess.

Description

  • Moved downlevel polyfills into src/libraries/Common/src/Polyfills/ and updated multiple libraries to call "modern" APIs unconditionally (e.g., Stopwatch.GetElapsedTime, double.IsFinite, Array.MaxLength), relying on polyfills for non-.NETCoreApp targets to reduce #if usage.
  • Replaced many #if NET / #else code paths with unified API calls and added corresponding polyfills via $(CommonPath)Polyfills\*.cs.
  • Removed library-local polyfill helpers (e.g., JsonHelpers.IsFinite, System.Collections.Immutable's System/Polyfills.cs) in favor of shared Common polyfills.
  • Updated project files to link in the required polyfill sources for non-.NETCoreApp target frameworks.
  • Moved src/libraries/Common/tests/System/Collections/DictionaryExtensions.cs into src/libraries/Common/src/Polyfills/DictionaryPolyfills.cs to consolidate duplicate TryAdd implementations.
  • Moved EncodingPolyfills.cs from src/libraries/Common/src/System/Text/ to src/libraries/Common/src/Polyfills/ for consistent directory structure; updated references in Directory.Build.targets and Microsoft.Extensions.Logging.Console.csproj.
  • Cached Environment.ProcessId in EnvironmentPolyfills using a static field to avoid creating and disposing a Process object on every access on downlevel TFMs.

EgorBoand others added 5 commits March 30, 2026 02:49
…e IsFinite
- Revert OperatingSystem.Is* polyfills (keep #if NET guards)
- Replace more 0x7FFFFFC7 constants with Array.MaxLength via polyfill
(FrozenHashTable 2nd occurrence, CborWriter, JsonDocument.MetadataDb)
- Inline JsonHelpers.IsFinite into call sites (double.IsFinite/float.IsFinite)
- Inline Base2ExponentialHistogramAggregator.IsFinite into call sites
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split into IReadOnlySetPolyfill.cs and BitOperationsPolyfill.cs.
Both use #if !NET guards since the types don't exist on downlevel.
Update both src and test csproj references.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split into BitConverterPolyfills.cs and GuidPolyfills.cs for reuse
across libraries.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 30, 2026 01:54
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-meta
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Moves downlevel polyfills into src/libraries/Common/src/Polyfills/ and updates multiple libraries to call the “modern” APIs unconditionally (e.g., Stopwatch.GetElapsedTime, double.IsFinite, Array.MaxLength), relying on polyfills for non-.NETCoreApp targets to reduce #if usage.

Changes:

  • Replaced many #if NET / #else code paths with unified API calls and added corresponding polyfills via $(CommonPath)Polyfills\*.cs.
  • Removed library-local polyfill helpers (e.g., JsonHelpers.IsFinite, System.Collections.Immutable’s System/Polyfills.cs) in favor of shared Common polyfills.
  • Updated project files to link in the required polyfill sources for non-.NETCoreApp target frameworks.

Reviewed changes

Copilot reviewed 45 out of 45 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.csUses Stopwatch.GetElapsedTime unconditionally (polyfilled for downlevel).
src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csprojLinks StopwatchPolyfills.cs for non-.NETCoreApp builds.
src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.csSwitches finite checks to double.IsFinite / float.IsFinite.
src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.csSwitches finite checks to double.IsFinite / float.IsFinite.
src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.csRemoves local IsFinite helpers and BitArray.HasAllSet polyfill.
src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.MetadataDb.csUses Array.MaxLength instead of hardcoded constant.
src/libraries/System.Text.Json/src/System.Text.Json.csprojLinks Double/Single/BitArray/Array polyfills for non-.NETCoreApp.
src/libraries/System.Speech/src/System.Speech.csprojLinks StreamPolyfills.cs for non-.NETCoreApp.
src/libraries/System.Speech/src/Internal/Synthesis/EngineSite.csReplaces manual read loop with Stream.ReadExactly.
src/libraries/System.Speech/src/Internal/Synthesis/AudioBase.csReplaces manual read loop with Stream.ReadExactly.
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csprojReplaces in-project polyfills with linked Common BitConverter/Guid polyfills.
src/libraries/System.IO.Hashing/src/System/IO/Hashing/NonCryptographicHashAlgorithm.csUses CopyToAsync(destination, cancellationToken) overload unconditionally (polyfilled downlevel).
src/libraries/System.IO.Hashing/src/System.IO.Hashing.csprojLinks StreamPolyfills.cs for non-.NETCoreApp.
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.csUses Array.MaxLength unconditionally.
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Tag.csUses DateTimeOffset.TotalOffsetMinutes unconditionally (polyfilled downlevel).
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/CborConformanceLevel.csUses HashCode.AddBytes unconditionally (polyfilled downlevel).
src/libraries/System.Formats.Cbor/src/System.Formats.Cbor.csprojLinks DateTimeOffset/HashCode/Array polyfills for non-.NETCoreApp.
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Base2ExponentialHistogramAggregator.csUses double.IsFinite directly; removes local helper.
src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csprojLinks DoublePolyfills.cs for non-.NETCoreApp.
src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csprojReplaces product-local polyfills include with linked Common polyfills in tests.
src/libraries/System.Collections.Immutable/src/System/Polyfills.csRemoves library-local polyfills file.
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenHashTable.csUses Array.MaxLength unconditionally.
src/libraries/System.Collections.Immutable/src/System.Collections.Immutable.csprojRemoves System\Polyfills.cs; links Common polyfills for non-.NETCoreApp.
src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.csSimplifies conditional IsAndroidOrAppleMobile expression body.
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csprojLinks TextWriter/Encoding polyfills for non-.NETCoreApp.
src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.csRemoves unsafe GetChars fallback in favor of polyfilled overloads; simplifies char handling.
src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiParsingLogConsole.csUses TextWriter.Write(ReadOnlySpan<char>) unconditionally (polyfilled downlevel).
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/SystemdHelpers.csUses Environment.ProcessId unconditionally (polyfilled downlevel).
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/Microsoft.Extensions.Hosting.Systemd.csprojLinks EnvironmentPolyfills.cs for non-.NETCoreApp.
src/libraries/Common/src/Polyfills/TextWriterPolyfills.csAdds downlevel TextWriter.Write(ReadOnlySpan<char>).
src/libraries/Common/src/Polyfills/StreamPolyfills.csAdds downlevel Stream.ReadExactly(byte[]) and CopyToAsync(Stream, CancellationToken).
src/libraries/Common/src/Polyfills/StopwatchPolyfills.csAdds downlevel Stopwatch.GetElapsedTime overloads.
src/libraries/Common/src/Polyfills/SinglePolyfills.csAdds downlevel float.IsFinite.
src/libraries/Common/src/Polyfills/DoublePolyfills.csAdds downlevel double.IsFinite.
src/libraries/Common/src/Polyfills/BitArrayPolyfills.csAdds downlevel BitArray.HasAllSet().
src/libraries/Common/src/Polyfills/ArrayPolyfills.csAdds downlevel Array.MaxLength.
src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.csAdds downlevel DateTimeOffset.TotalOffsetMinutes.
src/libraries/Common/src/Polyfills/HashCodePolyfills.csAdds downlevel HashCode.AddBytes(ReadOnlySpan<byte>) as an extension method.
src/libraries/Common/src/Polyfills/EnvironmentPolyfills.csAdds downlevel Environment.ProcessId.
src/libraries/Common/src/Polyfills/GuidPolyfills.csAdds downlevel Guid.TryWriteBytes(Span<byte>).
src/libraries/Common/src/Polyfills/BitConverterPolyfills.csAdds downlevel BitConverter.SingleToUInt32Bits / DoubleToUInt64Bits.
src/libraries/Common/src/Polyfills/IReadOnlySetPolyfill.csAdds downlevel IReadOnlySet<T> (guarded by #if !NET).
src/libraries/Common/src/Polyfills/BitOperationsPolyfill.csAdds downlevel System.Numerics.BitOperations.RotateLeft (guarded by #if !NET).
src/libraries/Common/src/Polyfills/CollectionsPolyfills.csAdds downlevel KeyValuePair.Deconstruct extension method.
src/libraries/Common/src/Polyfills/DictionaryPolyfills.csAdds downlevel TryAdd / TryDequeue polyfills (new shared source).

Comment threadsrc/libraries/Common/src/Polyfills/EnvironmentPolyfills.cs Outdated
@github-actions

This comment has been minimized.

Leverage polyfills already in Common/src/Polyfills/ to remove #if NET
blocks across additional libraries:
- ArrayPolyfills: LengthBuckets.cs, ArrayInfo.cs (Nrbf)
- IReadOnlySetPolyfill: ImmutableSortedSet, ImmutableHashSet, FrozenSet
- StreamPolyfills: SoundPlayer.cs, XmlBuffer.cs (Syndication)
- DateTimeOffsetPolyfills: CacheEntry.cs, Rss20FeedFormatter, Atom10FeedFormatter
- HashCodePolyfills: CoseHeaderValue.cs
- EnvironmentPolyfills: Win32.cs (Hosting.WindowsServices), SystemdHelpers.cs cleanup
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

CopilotAI review requested due to automatic review settings March 30, 2026 02:49
- EnvironmentPolyfills: dispose Process instance to avoid handle leak
- DoublePolyfills/SinglePolyfills: use same bit-manipulation impl as
the real Double.IsFinite/Single.IsFinite in CoreLib
- IReadOnlySetPolyfill: add comment explaining why #if !NET is needed
- StreamPolyfills: add comment explaining the 81_920 buffer size constant
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New polyfill: RuntimeHelpersPolyfills.cs provides:
- TryEnsureSufficientExecutionStack (wraps EnsureSufficientExecutionStack)
- GetUninitializedObject (delegates to FormatterServices)
Cleaned up in System.Text.Json:
- StackHelper.cs: remove #if NET for TryEnsureSufficientExecutionStack
- FSharpUnionConverter.cs: remove #if NET for GetUninitializedObject
and dead conditional using
Skipped (not polyfillable with extension() blocks):
- JsonArray.cs: List.AddRange(ReadOnlySpan) and uint.TryFormat
- CharConverter.cs: new ReadOnlySpan<char>(in value) constructor
- JsonCamelCaseNamingPolicy.cs: string.Create (shared with generators)
- JsonReaderHelper.Unescaping.cs: Utf8.IsValid (different error handling)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 66 out of 66 changed files in this pull request and generated 2 comments.

Comment threadsrc/libraries/System.Text.Json/src/System/Text/Json/StackHelper.cs Outdated
@github-actions

This comment has been minimized.

- Fix stray carriage return in Caching.Memory csproj
- JsonConsoleFormatter: use stackalloc span for char writing instead of
ToString() to avoid allocation regression (copilot review)
- Delete StackHelper.cs, inline RuntimeHelpers.TryEnsureSufficientExecutionStack
call directly in JsonElement.cs (jkotas)
- Revert IReadOnlySet polyfill: restore #if NET guards on FrozenSet,
ImmutableHashSet, ImmutableSortedSet; keep IReadOnlySet<T> definition
in library-local Polyfills.cs (jkotas concern about subtle bugs)
- Rename BitOperationsPolyfill.cs -> BitOperationsPolyfills.cs for
consistent plural naming across all polyfill files
- Remove dead StreamPolyfills ref from System.Windows.Extensions
(only targets NETCoreApp, polyfill would never compile)
- Fix EncodingPolyfills Link path for directory structure consistency
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

- Fully revert all changes to SimpleConsoleFormatter.cs
- JsonConsoleFormatter: use [charValue] collection literal instead of
stackalloc + manual assignment
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 30, 2026 14:36
@EgorBo
EgorBo removed the request for review from CopilotMarch 31, 2026 13:27

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Observation — EncodingPolyfills.cs lives outside Polyfills/ directory
EncodingPolyfills.cs is at $(CommonPath)System\Text\EncodingPolyfills.cs (pre-existing file not moved by this PR) but is l...

Done in 0f64157. Moved EncodingPolyfills.cs from src/libraries/Common/src/System/Text/ to src/libraries/Common/src/Polyfills/, updated the file to use a file-scoped namespace, and updated the references in both Directory.Build.targets and Microsoft.Extensions.Logging.Console.csproj.

CopilotAI changed the title Move pollyfills for !NET targets to src/libraries/Common/src/Polyfills/Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/Mar 31, 2026
CopilotAI requested a review from jkotasMarch 31, 2026 13:29
@EgorBo

EgorBo commented Mar 31, 2026

Copy link
Copy Markdown
MemberAuthor

Ah, I was not aware it finally is able to push into developers' PRs 🙂 . Although, I guess it's possible only because my local Copilot filed this PR in dotnet/runtime and not in EgorBo/runtime

@jkotas

Copy link
Copy Markdown
Member

Right, it works only for PRs in dotnet/runtime currently.

@jkotas

Copy link
Copy Markdown
Member

The polyfill creates and disposes a Process object on every access. This matches the pattern used by the old inline #else code and only runs on downlevel TFMs where Environment.ProcessId doesn't exist. On .NETCoreApp the real property is used instead (zero-cost)

@copilot Address this feedback by adding a simple cache

@EgorBo

Copy link
Copy Markdown
MemberAuthor

@copilot Address this feedback by adding a simple cache

I wasn't sure it in and left it as is. Actually, my impl is even better than whatever we had before - we didn't even dispose process objects.

CopilotAI review requested due to automatic review settings March 31, 2026 15:58
@EgorBo
EgorBo removed the request for review from CopilotMarch 31, 2026 15:58

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

The polyfill creates and disposes a Process object on every access. This matches the pattern used by the old inline #else code and only runs on downlevel TFMs where Environment.ProcessId doesn't exi...

Done in f0bf71e. Added a s_processId static field initialized to 0 (not a valid PID) as the cache sentinel — the Process object is now only created once per process lifetime.

CopilotAI requested a review from jkotasMarch 31, 2026 15:59
@jkotas

Copy link
Copy Markdown
Member

I wasn't sure it in and left it as is

I think we want to avoid bad surprises when using polyfills.

@EgorBo
EgorBo enabled auto-merge (squash) April 1, 2026 00:27
@EgorBo

Copy link
Copy Markdown
MemberAuthor

/ba-g deadletter (WasmTestOnV8)

@jkotas

Copy link
Copy Markdown
Member

I have asked copilot to look for more https://github.com/dotnet/runtime/tasks/8e893e7e-9ea0-4c23-9402-5d3b9a8a0af2

jkotas added a commit that referenced this pull request Apr 1, 2026
Follow-up to #126287 — consolidates library-local `BitOperations`,
`Stream`, `StringBuilder`, and `Stack` polyfills under
`src/libraries/Common/src/Polyfills/`.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 1, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@EgorBo@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/ - #126287

Merged
EgorBo merged 22 commits into
mainfrom
common-pollyfills
Apr 1, 2026
Merged

Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/#126287
EgorBo merged 22 commits into
mainfrom
common-pollyfills

Conversation

@EgorBo

@EgorBoEgorBo commented Mar 30, 2026

Copy link
Copy Markdown
Member

This allows us to pretend new APIs exist for .NET Standard 2.0/NETFX/etc targets and avoid #if NET else mess.

Description

  • Moved downlevel polyfills into src/libraries/Common/src/Polyfills/ and updated multiple libraries to call "modern" APIs unconditionally (e.g., Stopwatch.GetElapsedTime, double.IsFinite, Array.MaxLength), relying on polyfills for non-.NETCoreApp targets to reduce #if usage.
  • Replaced many #if NET / #else code paths with unified API calls and added corresponding polyfills via $(CommonPath)Polyfills\*.cs.
  • Removed library-local polyfill helpers (e.g., JsonHelpers.IsFinite, System.Collections.Immutable's System/Polyfills.cs) in favor of shared Common polyfills.
  • Updated project files to link in the required polyfill sources for non-.NETCoreApp target frameworks.
  • Moved src/libraries/Common/tests/System/Collections/DictionaryExtensions.cs into src/libraries/Common/src/Polyfills/DictionaryPolyfills.cs to consolidate duplicate TryAdd implementations.
  • Moved EncodingPolyfills.cs from src/libraries/Common/src/System/Text/ to src/libraries/Common/src/Polyfills/ for consistent directory structure; updated references in Directory.Build.targets and Microsoft.Extensions.Logging.Console.csproj.
  • Cached Environment.ProcessId in EnvironmentPolyfills using a static field to avoid creating and disposing a Process object on every access on downlevel TFMs.

EgorBoand others added 5 commits March 30, 2026 02:49
…e IsFinite
- Revert OperatingSystem.Is* polyfills (keep #if NET guards)
- Replace more 0x7FFFFFC7 constants with Array.MaxLength via polyfill
(FrozenHashTable 2nd occurrence, CborWriter, JsonDocument.MetadataDb)
- Inline JsonHelpers.IsFinite into call sites (double.IsFinite/float.IsFinite)
- Inline Base2ExponentialHistogramAggregator.IsFinite into call sites
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split into IReadOnlySetPolyfill.cs and BitOperationsPolyfill.cs.
Both use #if !NET guards since the types don't exist on downlevel.
Update both src and test csproj references.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split into BitConverterPolyfills.cs and GuidPolyfills.cs for reuse
across libraries.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 30, 2026 01:54
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-meta
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Moves downlevel polyfills into src/libraries/Common/src/Polyfills/ and updates multiple libraries to call the “modern” APIs unconditionally (e.g., Stopwatch.GetElapsedTime, double.IsFinite, Array.MaxLength), relying on polyfills for non-.NETCoreApp targets to reduce #if usage.

Changes:

  • Replaced many #if NET / #else code paths with unified API calls and added corresponding polyfills via $(CommonPath)Polyfills\*.cs.
  • Removed library-local polyfill helpers (e.g., JsonHelpers.IsFinite, System.Collections.Immutable’s System/Polyfills.cs) in favor of shared Common polyfills.
  • Updated project files to link in the required polyfill sources for non-.NETCoreApp target frameworks.

Reviewed changes

Copilot reviewed 45 out of 45 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.csUses Stopwatch.GetElapsedTime unconditionally (polyfilled for downlevel).
src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csprojLinks StopwatchPolyfills.cs for non-.NETCoreApp builds.
src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.csSwitches finite checks to double.IsFinite / float.IsFinite.
src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.csSwitches finite checks to double.IsFinite / float.IsFinite.
src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.csRemoves local IsFinite helpers and BitArray.HasAllSet polyfill.
src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.MetadataDb.csUses Array.MaxLength instead of hardcoded constant.
src/libraries/System.Text.Json/src/System.Text.Json.csprojLinks Double/Single/BitArray/Array polyfills for non-.NETCoreApp.
src/libraries/System.Speech/src/System.Speech.csprojLinks StreamPolyfills.cs for non-.NETCoreApp.
src/libraries/System.Speech/src/Internal/Synthesis/EngineSite.csReplaces manual read loop with Stream.ReadExactly.
src/libraries/System.Speech/src/Internal/Synthesis/AudioBase.csReplaces manual read loop with Stream.ReadExactly.
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csprojReplaces in-project polyfills with linked Common BitConverter/Guid polyfills.
src/libraries/System.IO.Hashing/src/System/IO/Hashing/NonCryptographicHashAlgorithm.csUses CopyToAsync(destination, cancellationToken) overload unconditionally (polyfilled downlevel).
src/libraries/System.IO.Hashing/src/System.IO.Hashing.csprojLinks StreamPolyfills.cs for non-.NETCoreApp.
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.csUses Array.MaxLength unconditionally.
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Tag.csUses DateTimeOffset.TotalOffsetMinutes unconditionally (polyfilled downlevel).
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/CborConformanceLevel.csUses HashCode.AddBytes unconditionally (polyfilled downlevel).
src/libraries/System.Formats.Cbor/src/System.Formats.Cbor.csprojLinks DateTimeOffset/HashCode/Array polyfills for non-.NETCoreApp.
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Base2ExponentialHistogramAggregator.csUses double.IsFinite directly; removes local helper.
src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csprojLinks DoublePolyfills.cs for non-.NETCoreApp.
src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csprojReplaces product-local polyfills include with linked Common polyfills in tests.
src/libraries/System.Collections.Immutable/src/System/Polyfills.csRemoves library-local polyfills file.
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenHashTable.csUses Array.MaxLength unconditionally.
src/libraries/System.Collections.Immutable/src/System.Collections.Immutable.csprojRemoves System\Polyfills.cs; links Common polyfills for non-.NETCoreApp.
src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.csSimplifies conditional IsAndroidOrAppleMobile expression body.
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csprojLinks TextWriter/Encoding polyfills for non-.NETCoreApp.
src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.csRemoves unsafe GetChars fallback in favor of polyfilled overloads; simplifies char handling.
src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiParsingLogConsole.csUses TextWriter.Write(ReadOnlySpan<char>) unconditionally (polyfilled downlevel).
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/SystemdHelpers.csUses Environment.ProcessId unconditionally (polyfilled downlevel).
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/Microsoft.Extensions.Hosting.Systemd.csprojLinks EnvironmentPolyfills.cs for non-.NETCoreApp.
src/libraries/Common/src/Polyfills/TextWriterPolyfills.csAdds downlevel TextWriter.Write(ReadOnlySpan<char>).
src/libraries/Common/src/Polyfills/StreamPolyfills.csAdds downlevel Stream.ReadExactly(byte[]) and CopyToAsync(Stream, CancellationToken).
src/libraries/Common/src/Polyfills/StopwatchPolyfills.csAdds downlevel Stopwatch.GetElapsedTime overloads.
src/libraries/Common/src/Polyfills/SinglePolyfills.csAdds downlevel float.IsFinite.
src/libraries/Common/src/Polyfills/DoublePolyfills.csAdds downlevel double.IsFinite.
src/libraries/Common/src/Polyfills/BitArrayPolyfills.csAdds downlevel BitArray.HasAllSet().
src/libraries/Common/src/Polyfills/ArrayPolyfills.csAdds downlevel Array.MaxLength.
src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.csAdds downlevel DateTimeOffset.TotalOffsetMinutes.
src/libraries/Common/src/Polyfills/HashCodePolyfills.csAdds downlevel HashCode.AddBytes(ReadOnlySpan<byte>) as an extension method.
src/libraries/Common/src/Polyfills/EnvironmentPolyfills.csAdds downlevel Environment.ProcessId.
src/libraries/Common/src/Polyfills/GuidPolyfills.csAdds downlevel Guid.TryWriteBytes(Span<byte>).
src/libraries/Common/src/Polyfills/BitConverterPolyfills.csAdds downlevel BitConverter.SingleToUInt32Bits / DoubleToUInt64Bits.
src/libraries/Common/src/Polyfills/IReadOnlySetPolyfill.csAdds downlevel IReadOnlySet<T> (guarded by #if !NET).
src/libraries/Common/src/Polyfills/BitOperationsPolyfill.csAdds downlevel System.Numerics.BitOperations.RotateLeft (guarded by #if !NET).
src/libraries/Common/src/Polyfills/CollectionsPolyfills.csAdds downlevel KeyValuePair.Deconstruct extension method.
src/libraries/Common/src/Polyfills/DictionaryPolyfills.csAdds downlevel TryAdd / TryDequeue polyfills (new shared source).

Comment threadsrc/libraries/Common/src/Polyfills/EnvironmentPolyfills.cs Outdated
@github-actions

This comment has been minimized.

Leverage polyfills already in Common/src/Polyfills/ to remove #if NET
blocks across additional libraries:
- ArrayPolyfills: LengthBuckets.cs, ArrayInfo.cs (Nrbf)
- IReadOnlySetPolyfill: ImmutableSortedSet, ImmutableHashSet, FrozenSet
- StreamPolyfills: SoundPlayer.cs, XmlBuffer.cs (Syndication)
- DateTimeOffsetPolyfills: CacheEntry.cs, Rss20FeedFormatter, Atom10FeedFormatter
- HashCodePolyfills: CoseHeaderValue.cs
- EnvironmentPolyfills: Win32.cs (Hosting.WindowsServices), SystemdHelpers.cs cleanup
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

CopilotAI review requested due to automatic review settings March 30, 2026 02:49
- EnvironmentPolyfills: dispose Process instance to avoid handle leak
- DoublePolyfills/SinglePolyfills: use same bit-manipulation impl as
the real Double.IsFinite/Single.IsFinite in CoreLib
- IReadOnlySetPolyfill: add comment explaining why #if !NET is needed
- StreamPolyfills: add comment explaining the 81_920 buffer size constant
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New polyfill: RuntimeHelpersPolyfills.cs provides:
- TryEnsureSufficientExecutionStack (wraps EnsureSufficientExecutionStack)
- GetUninitializedObject (delegates to FormatterServices)
Cleaned up in System.Text.Json:
- StackHelper.cs: remove #if NET for TryEnsureSufficientExecutionStack
- FSharpUnionConverter.cs: remove #if NET for GetUninitializedObject
and dead conditional using
Skipped (not polyfillable with extension() blocks):
- JsonArray.cs: List.AddRange(ReadOnlySpan) and uint.TryFormat
- CharConverter.cs: new ReadOnlySpan<char>(in value) constructor
- JsonCamelCaseNamingPolicy.cs: string.Create (shared with generators)
- JsonReaderHelper.Unescaping.cs: Utf8.IsValid (different error handling)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 66 out of 66 changed files in this pull request and generated 2 comments.

Comment threadsrc/libraries/System.Text.Json/src/System/Text/Json/StackHelper.cs Outdated
@github-actions

This comment has been minimized.

- Fix stray carriage return in Caching.Memory csproj
- JsonConsoleFormatter: use stackalloc span for char writing instead of
ToString() to avoid allocation regression (copilot review)
- Delete StackHelper.cs, inline RuntimeHelpers.TryEnsureSufficientExecutionStack
call directly in JsonElement.cs (jkotas)
- Revert IReadOnlySet polyfill: restore #if NET guards on FrozenSet,
ImmutableHashSet, ImmutableSortedSet; keep IReadOnlySet<T> definition
in library-local Polyfills.cs (jkotas concern about subtle bugs)
- Rename BitOperationsPolyfill.cs -> BitOperationsPolyfills.cs for
consistent plural naming across all polyfill files
- Remove dead StreamPolyfills ref from System.Windows.Extensions
(only targets NETCoreApp, polyfill would never compile)
- Fix EncodingPolyfills Link path for directory structure consistency
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

- Fully revert all changes to SimpleConsoleFormatter.cs
- JsonConsoleFormatter: use [charValue] collection literal instead of
stackalloc + manual assignment
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 30, 2026 14:36
@EgorBo
EgorBo removed the request for review from CopilotMarch 31, 2026 13:27

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Observation — EncodingPolyfills.cs lives outside Polyfills/ directory
EncodingPolyfills.cs is at $(CommonPath)System\Text\EncodingPolyfills.cs (pre-existing file not moved by this PR) but is l...

Done in 0f64157. Moved EncodingPolyfills.cs from src/libraries/Common/src/System/Text/ to src/libraries/Common/src/Polyfills/, updated the file to use a file-scoped namespace, and updated the references in both Directory.Build.targets and Microsoft.Extensions.Logging.Console.csproj.

CopilotAI changed the title Move pollyfills for !NET targets to src/libraries/Common/src/Polyfills/Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/Mar 31, 2026
CopilotAI requested a review from jkotasMarch 31, 2026 13:29
@EgorBo

EgorBo commented Mar 31, 2026

Copy link
Copy Markdown
MemberAuthor

Ah, I was not aware it finally is able to push into developers' PRs 🙂 . Although, I guess it's possible only because my local Copilot filed this PR in dotnet/runtime and not in EgorBo/runtime

@jkotas

Copy link
Copy Markdown
Member

Right, it works only for PRs in dotnet/runtime currently.

@jkotas

Copy link
Copy Markdown
Member

The polyfill creates and disposes a Process object on every access. This matches the pattern used by the old inline #else code and only runs on downlevel TFMs where Environment.ProcessId doesn't exist. On .NETCoreApp the real property is used instead (zero-cost)

@copilot Address this feedback by adding a simple cache

@EgorBo

Copy link
Copy Markdown
MemberAuthor

@copilot Address this feedback by adding a simple cache

I wasn't sure it in and left it as is. Actually, my impl is even better than whatever we had before - we didn't even dispose process objects.

CopilotAI review requested due to automatic review settings March 31, 2026 15:58
@EgorBo
EgorBo removed the request for review from CopilotMarch 31, 2026 15:58

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

The polyfill creates and disposes a Process object on every access. This matches the pattern used by the old inline #else code and only runs on downlevel TFMs where Environment.ProcessId doesn't exi...

Done in f0bf71e. Added a s_processId static field initialized to 0 (not a valid PID) as the cache sentinel — the Process object is now only created once per process lifetime.

CopilotAI requested a review from jkotasMarch 31, 2026 15:59
@jkotas

Copy link
Copy Markdown
Member

I wasn't sure it in and left it as is

I think we want to avoid bad surprises when using polyfills.

@EgorBo
EgorBo enabled auto-merge (squash) April 1, 2026 00:27
@EgorBo

Copy link
Copy Markdown
MemberAuthor

/ba-g deadletter (WasmTestOnV8)

@jkotas

Copy link
Copy Markdown
Member

I have asked copilot to look for more https://github.com/dotnet/runtime/tasks/8e893e7e-9ea0-4c23-9402-5d3b9a8a0af2

jkotas added a commit that referenced this pull request Apr 1, 2026
Follow-up to #126287 — consolidates library-local `BitOperations`,
`Stream`, `StringBuilder`, and `Stack` polyfills under
`src/libraries/Common/src/Polyfills/`.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 1, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@EgorBo@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/ - #126287

Merged
EgorBo merged 22 commits into
mainfrom
common-pollyfills
Apr 1, 2026
Merged

Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/#126287
EgorBo merged 22 commits into
mainfrom
common-pollyfills

Conversation

@EgorBo

@EgorBoEgorBo commented Mar 30, 2026

Copy link
Copy Markdown
Member

This allows us to pretend new APIs exist for .NET Standard 2.0/NETFX/etc targets and avoid #if NET else mess.

Description

  • Moved downlevel polyfills into src/libraries/Common/src/Polyfills/ and updated multiple libraries to call "modern" APIs unconditionally (e.g., Stopwatch.GetElapsedTime, double.IsFinite, Array.MaxLength), relying on polyfills for non-.NETCoreApp targets to reduce #if usage.
  • Replaced many #if NET / #else code paths with unified API calls and added corresponding polyfills via $(CommonPath)Polyfills\*.cs.
  • Removed library-local polyfill helpers (e.g., JsonHelpers.IsFinite, System.Collections.Immutable's System/Polyfills.cs) in favor of shared Common polyfills.
  • Updated project files to link in the required polyfill sources for non-.NETCoreApp target frameworks.
  • Moved src/libraries/Common/tests/System/Collections/DictionaryExtensions.cs into src/libraries/Common/src/Polyfills/DictionaryPolyfills.cs to consolidate duplicate TryAdd implementations.
  • Moved EncodingPolyfills.cs from src/libraries/Common/src/System/Text/ to src/libraries/Common/src/Polyfills/ for consistent directory structure; updated references in Directory.Build.targets and Microsoft.Extensions.Logging.Console.csproj.
  • Cached Environment.ProcessId in EnvironmentPolyfills using a static field to avoid creating and disposing a Process object on every access on downlevel TFMs.

EgorBoand others added 5 commits March 30, 2026 02:49
…e IsFinite
- Revert OperatingSystem.Is* polyfills (keep #if NET guards)
- Replace more 0x7FFFFFC7 constants with Array.MaxLength via polyfill
(FrozenHashTable 2nd occurrence, CborWriter, JsonDocument.MetadataDb)
- Inline JsonHelpers.IsFinite into call sites (double.IsFinite/float.IsFinite)
- Inline Base2ExponentialHistogramAggregator.IsFinite into call sites
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split into IReadOnlySetPolyfill.cs and BitOperationsPolyfill.cs.
Both use #if !NET guards since the types don't exist on downlevel.
Update both src and test csproj references.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split into BitConverterPolyfills.cs and GuidPolyfills.cs for reuse
across libraries.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 30, 2026 01:54
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-meta
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Moves downlevel polyfills into src/libraries/Common/src/Polyfills/ and updates multiple libraries to call the “modern” APIs unconditionally (e.g., Stopwatch.GetElapsedTime, double.IsFinite, Array.MaxLength), relying on polyfills for non-.NETCoreApp targets to reduce #if usage.

Changes:

  • Replaced many #if NET / #else code paths with unified API calls and added corresponding polyfills via $(CommonPath)Polyfills\*.cs.
  • Removed library-local polyfill helpers (e.g., JsonHelpers.IsFinite, System.Collections.Immutable’s System/Polyfills.cs) in favor of shared Common polyfills.
  • Updated project files to link in the required polyfill sources for non-.NETCoreApp target frameworks.

Reviewed changes

Copilot reviewed 45 out of 45 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.csUses Stopwatch.GetElapsedTime unconditionally (polyfilled for downlevel).
src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csprojLinks StopwatchPolyfills.cs for non-.NETCoreApp builds.
src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.csSwitches finite checks to double.IsFinite / float.IsFinite.
src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.csSwitches finite checks to double.IsFinite / float.IsFinite.
src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.csRemoves local IsFinite helpers and BitArray.HasAllSet polyfill.
src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.MetadataDb.csUses Array.MaxLength instead of hardcoded constant.
src/libraries/System.Text.Json/src/System.Text.Json.csprojLinks Double/Single/BitArray/Array polyfills for non-.NETCoreApp.
src/libraries/System.Speech/src/System.Speech.csprojLinks StreamPolyfills.cs for non-.NETCoreApp.
src/libraries/System.Speech/src/Internal/Synthesis/EngineSite.csReplaces manual read loop with Stream.ReadExactly.
src/libraries/System.Speech/src/Internal/Synthesis/AudioBase.csReplaces manual read loop with Stream.ReadExactly.
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csprojReplaces in-project polyfills with linked Common BitConverter/Guid polyfills.
src/libraries/System.IO.Hashing/src/System/IO/Hashing/NonCryptographicHashAlgorithm.csUses CopyToAsync(destination, cancellationToken) overload unconditionally (polyfilled downlevel).
src/libraries/System.IO.Hashing/src/System.IO.Hashing.csprojLinks StreamPolyfills.cs for non-.NETCoreApp.
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.csUses Array.MaxLength unconditionally.
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Tag.csUses DateTimeOffset.TotalOffsetMinutes unconditionally (polyfilled downlevel).
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/CborConformanceLevel.csUses HashCode.AddBytes unconditionally (polyfilled downlevel).
src/libraries/System.Formats.Cbor/src/System.Formats.Cbor.csprojLinks DateTimeOffset/HashCode/Array polyfills for non-.NETCoreApp.
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Base2ExponentialHistogramAggregator.csUses double.IsFinite directly; removes local helper.
src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csprojLinks DoublePolyfills.cs for non-.NETCoreApp.
src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csprojReplaces product-local polyfills include with linked Common polyfills in tests.
src/libraries/System.Collections.Immutable/src/System/Polyfills.csRemoves library-local polyfills file.
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenHashTable.csUses Array.MaxLength unconditionally.
src/libraries/System.Collections.Immutable/src/System.Collections.Immutable.csprojRemoves System\Polyfills.cs; links Common polyfills for non-.NETCoreApp.
src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.csSimplifies conditional IsAndroidOrAppleMobile expression body.
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csprojLinks TextWriter/Encoding polyfills for non-.NETCoreApp.
src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.csRemoves unsafe GetChars fallback in favor of polyfilled overloads; simplifies char handling.
src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiParsingLogConsole.csUses TextWriter.Write(ReadOnlySpan<char>) unconditionally (polyfilled downlevel).
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/SystemdHelpers.csUses Environment.ProcessId unconditionally (polyfilled downlevel).
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/Microsoft.Extensions.Hosting.Systemd.csprojLinks EnvironmentPolyfills.cs for non-.NETCoreApp.
src/libraries/Common/src/Polyfills/TextWriterPolyfills.csAdds downlevel TextWriter.Write(ReadOnlySpan<char>).
src/libraries/Common/src/Polyfills/StreamPolyfills.csAdds downlevel Stream.ReadExactly(byte[]) and CopyToAsync(Stream, CancellationToken).
src/libraries/Common/src/Polyfills/StopwatchPolyfills.csAdds downlevel Stopwatch.GetElapsedTime overloads.
src/libraries/Common/src/Polyfills/SinglePolyfills.csAdds downlevel float.IsFinite.
src/libraries/Common/src/Polyfills/DoublePolyfills.csAdds downlevel double.IsFinite.
src/libraries/Common/src/Polyfills/BitArrayPolyfills.csAdds downlevel BitArray.HasAllSet().
src/libraries/Common/src/Polyfills/ArrayPolyfills.csAdds downlevel Array.MaxLength.
src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.csAdds downlevel DateTimeOffset.TotalOffsetMinutes.
src/libraries/Common/src/Polyfills/HashCodePolyfills.csAdds downlevel HashCode.AddBytes(ReadOnlySpan<byte>) as an extension method.
src/libraries/Common/src/Polyfills/EnvironmentPolyfills.csAdds downlevel Environment.ProcessId.
src/libraries/Common/src/Polyfills/GuidPolyfills.csAdds downlevel Guid.TryWriteBytes(Span<byte>).
src/libraries/Common/src/Polyfills/BitConverterPolyfills.csAdds downlevel BitConverter.SingleToUInt32Bits / DoubleToUInt64Bits.
src/libraries/Common/src/Polyfills/IReadOnlySetPolyfill.csAdds downlevel IReadOnlySet<T> (guarded by #if !NET).
src/libraries/Common/src/Polyfills/BitOperationsPolyfill.csAdds downlevel System.Numerics.BitOperations.RotateLeft (guarded by #if !NET).
src/libraries/Common/src/Polyfills/CollectionsPolyfills.csAdds downlevel KeyValuePair.Deconstruct extension method.
src/libraries/Common/src/Polyfills/DictionaryPolyfills.csAdds downlevel TryAdd / TryDequeue polyfills (new shared source).

Comment threadsrc/libraries/Common/src/Polyfills/EnvironmentPolyfills.cs Outdated
@github-actions

This comment has been minimized.

Leverage polyfills already in Common/src/Polyfills/ to remove #if NET
blocks across additional libraries:
- ArrayPolyfills: LengthBuckets.cs, ArrayInfo.cs (Nrbf)
- IReadOnlySetPolyfill: ImmutableSortedSet, ImmutableHashSet, FrozenSet
- StreamPolyfills: SoundPlayer.cs, XmlBuffer.cs (Syndication)
- DateTimeOffsetPolyfills: CacheEntry.cs, Rss20FeedFormatter, Atom10FeedFormatter
- HashCodePolyfills: CoseHeaderValue.cs
- EnvironmentPolyfills: Win32.cs (Hosting.WindowsServices), SystemdHelpers.cs cleanup
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

CopilotAI review requested due to automatic review settings March 30, 2026 02:49
- EnvironmentPolyfills: dispose Process instance to avoid handle leak
- DoublePolyfills/SinglePolyfills: use same bit-manipulation impl as
the real Double.IsFinite/Single.IsFinite in CoreLib
- IReadOnlySetPolyfill: add comment explaining why #if !NET is needed
- StreamPolyfills: add comment explaining the 81_920 buffer size constant
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New polyfill: RuntimeHelpersPolyfills.cs provides:
- TryEnsureSufficientExecutionStack (wraps EnsureSufficientExecutionStack)
- GetUninitializedObject (delegates to FormatterServices)
Cleaned up in System.Text.Json:
- StackHelper.cs: remove #if NET for TryEnsureSufficientExecutionStack
- FSharpUnionConverter.cs: remove #if NET for GetUninitializedObject
and dead conditional using
Skipped (not polyfillable with extension() blocks):
- JsonArray.cs: List.AddRange(ReadOnlySpan) and uint.TryFormat
- CharConverter.cs: new ReadOnlySpan<char>(in value) constructor
- JsonCamelCaseNamingPolicy.cs: string.Create (shared with generators)
- JsonReaderHelper.Unescaping.cs: Utf8.IsValid (different error handling)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 66 out of 66 changed files in this pull request and generated 2 comments.

Comment threadsrc/libraries/System.Text.Json/src/System/Text/Json/StackHelper.cs Outdated
@github-actions

This comment has been minimized.

- Fix stray carriage return in Caching.Memory csproj
- JsonConsoleFormatter: use stackalloc span for char writing instead of
ToString() to avoid allocation regression (copilot review)
- Delete StackHelper.cs, inline RuntimeHelpers.TryEnsureSufficientExecutionStack
call directly in JsonElement.cs (jkotas)
- Revert IReadOnlySet polyfill: restore #if NET guards on FrozenSet,
ImmutableHashSet, ImmutableSortedSet; keep IReadOnlySet<T> definition
in library-local Polyfills.cs (jkotas concern about subtle bugs)
- Rename BitOperationsPolyfill.cs -> BitOperationsPolyfills.cs for
consistent plural naming across all polyfill files
- Remove dead StreamPolyfills ref from System.Windows.Extensions
(only targets NETCoreApp, polyfill would never compile)
- Fix EncodingPolyfills Link path for directory structure consistency
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

- Fully revert all changes to SimpleConsoleFormatter.cs
- JsonConsoleFormatter: use [charValue] collection literal instead of
stackalloc + manual assignment
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 30, 2026 14:36
@EgorBo
EgorBo removed the request for review from CopilotMarch 31, 2026 13:27

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Observation — EncodingPolyfills.cs lives outside Polyfills/ directory
EncodingPolyfills.cs is at $(CommonPath)System\Text\EncodingPolyfills.cs (pre-existing file not moved by this PR) but is l...

Done in 0f64157. Moved EncodingPolyfills.cs from src/libraries/Common/src/System/Text/ to src/libraries/Common/src/Polyfills/, updated the file to use a file-scoped namespace, and updated the references in both Directory.Build.targets and Microsoft.Extensions.Logging.Console.csproj.

CopilotAI changed the title Move pollyfills for !NET targets to src/libraries/Common/src/Polyfills/Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/Mar 31, 2026
CopilotAI requested a review from jkotasMarch 31, 2026 13:29
@EgorBo

EgorBo commented Mar 31, 2026

Copy link
Copy Markdown
MemberAuthor

Ah, I was not aware it finally is able to push into developers' PRs 🙂 . Although, I guess it's possible only because my local Copilot filed this PR in dotnet/runtime and not in EgorBo/runtime

@jkotas

Copy link
Copy Markdown
Member

Right, it works only for PRs in dotnet/runtime currently.

@jkotas

Copy link
Copy Markdown
Member

The polyfill creates and disposes a Process object on every access. This matches the pattern used by the old inline #else code and only runs on downlevel TFMs where Environment.ProcessId doesn't exist. On .NETCoreApp the real property is used instead (zero-cost)

@copilot Address this feedback by adding a simple cache

@EgorBo

Copy link
Copy Markdown
MemberAuthor

@copilot Address this feedback by adding a simple cache

I wasn't sure it in and left it as is. Actually, my impl is even better than whatever we had before - we didn't even dispose process objects.

CopilotAI review requested due to automatic review settings March 31, 2026 15:58
@EgorBo
EgorBo removed the request for review from CopilotMarch 31, 2026 15:58

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

The polyfill creates and disposes a Process object on every access. This matches the pattern used by the old inline #else code and only runs on downlevel TFMs where Environment.ProcessId doesn't exi...

Done in f0bf71e. Added a s_processId static field initialized to 0 (not a valid PID) as the cache sentinel — the Process object is now only created once per process lifetime.

CopilotAI requested a review from jkotasMarch 31, 2026 15:59
@jkotas

Copy link
Copy Markdown
Member

I wasn't sure it in and left it as is

I think we want to avoid bad surprises when using polyfills.

@EgorBo
EgorBo enabled auto-merge (squash) April 1, 2026 00:27
@EgorBo

Copy link
Copy Markdown
MemberAuthor

/ba-g deadletter (WasmTestOnV8)

@jkotas

Copy link
Copy Markdown
Member

I have asked copilot to look for more https://github.com/dotnet/runtime/tasks/8e893e7e-9ea0-4c23-9402-5d3b9a8a0af2

jkotas added a commit that referenced this pull request Apr 1, 2026
Follow-up to #126287 — consolidates library-local `BitOperations`,
`Stream`, `StringBuilder`, and `Stack` polyfills under
`src/libraries/Common/src/Polyfills/`.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 1, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@EgorBo@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/ - #126287

Merged
EgorBo merged 22 commits into
mainfrom
common-pollyfills
Apr 1, 2026
Merged

Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/#126287
EgorBo merged 22 commits into
mainfrom
common-pollyfills

Conversation

@EgorBo

@EgorBoEgorBo commented Mar 30, 2026

Copy link
Copy Markdown
Member

This allows us to pretend new APIs exist for .NET Standard 2.0/NETFX/etc targets and avoid #if NET else mess.

Description

  • Moved downlevel polyfills into src/libraries/Common/src/Polyfills/ and updated multiple libraries to call "modern" APIs unconditionally (e.g., Stopwatch.GetElapsedTime, double.IsFinite, Array.MaxLength), relying on polyfills for non-.NETCoreApp targets to reduce #if usage.
  • Replaced many #if NET / #else code paths with unified API calls and added corresponding polyfills via $(CommonPath)Polyfills\*.cs.
  • Removed library-local polyfill helpers (e.g., JsonHelpers.IsFinite, System.Collections.Immutable's System/Polyfills.cs) in favor of shared Common polyfills.
  • Updated project files to link in the required polyfill sources for non-.NETCoreApp target frameworks.
  • Moved src/libraries/Common/tests/System/Collections/DictionaryExtensions.cs into src/libraries/Common/src/Polyfills/DictionaryPolyfills.cs to consolidate duplicate TryAdd implementations.
  • Moved EncodingPolyfills.cs from src/libraries/Common/src/System/Text/ to src/libraries/Common/src/Polyfills/ for consistent directory structure; updated references in Directory.Build.targets and Microsoft.Extensions.Logging.Console.csproj.
  • Cached Environment.ProcessId in EnvironmentPolyfills using a static field to avoid creating and disposing a Process object on every access on downlevel TFMs.

EgorBoand others added 5 commits March 30, 2026 02:49
…e IsFinite
- Revert OperatingSystem.Is* polyfills (keep #if NET guards)
- Replace more 0x7FFFFFC7 constants with Array.MaxLength via polyfill
(FrozenHashTable 2nd occurrence, CborWriter, JsonDocument.MetadataDb)
- Inline JsonHelpers.IsFinite into call sites (double.IsFinite/float.IsFinite)
- Inline Base2ExponentialHistogramAggregator.IsFinite into call sites
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split into IReadOnlySetPolyfill.cs and BitOperationsPolyfill.cs.
Both use #if !NET guards since the types don't exist on downlevel.
Update both src and test csproj references.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split into BitConverterPolyfills.cs and GuidPolyfills.cs for reuse
across libraries.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 30, 2026 01:54
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-meta
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Moves downlevel polyfills into src/libraries/Common/src/Polyfills/ and updates multiple libraries to call the “modern” APIs unconditionally (e.g., Stopwatch.GetElapsedTime, double.IsFinite, Array.MaxLength), relying on polyfills for non-.NETCoreApp targets to reduce #if usage.

Changes:

  • Replaced many #if NET / #else code paths with unified API calls and added corresponding polyfills via $(CommonPath)Polyfills\*.cs.
  • Removed library-local polyfill helpers (e.g., JsonHelpers.IsFinite, System.Collections.Immutable’s System/Polyfills.cs) in favor of shared Common polyfills.
  • Updated project files to link in the required polyfill sources for non-.NETCoreApp target frameworks.

Reviewed changes

Copilot reviewed 45 out of 45 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.csUses Stopwatch.GetElapsedTime unconditionally (polyfilled for downlevel).
src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csprojLinks StopwatchPolyfills.cs for non-.NETCoreApp builds.
src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.csSwitches finite checks to double.IsFinite / float.IsFinite.
src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.csSwitches finite checks to double.IsFinite / float.IsFinite.
src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.csRemoves local IsFinite helpers and BitArray.HasAllSet polyfill.
src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.MetadataDb.csUses Array.MaxLength instead of hardcoded constant.
src/libraries/System.Text.Json/src/System.Text.Json.csprojLinks Double/Single/BitArray/Array polyfills for non-.NETCoreApp.
src/libraries/System.Speech/src/System.Speech.csprojLinks StreamPolyfills.cs for non-.NETCoreApp.
src/libraries/System.Speech/src/Internal/Synthesis/EngineSite.csReplaces manual read loop with Stream.ReadExactly.
src/libraries/System.Speech/src/Internal/Synthesis/AudioBase.csReplaces manual read loop with Stream.ReadExactly.
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csprojReplaces in-project polyfills with linked Common BitConverter/Guid polyfills.
src/libraries/System.IO.Hashing/src/System/IO/Hashing/NonCryptographicHashAlgorithm.csUses CopyToAsync(destination, cancellationToken) overload unconditionally (polyfilled downlevel).
src/libraries/System.IO.Hashing/src/System.IO.Hashing.csprojLinks StreamPolyfills.cs for non-.NETCoreApp.
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.csUses Array.MaxLength unconditionally.
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Tag.csUses DateTimeOffset.TotalOffsetMinutes unconditionally (polyfilled downlevel).
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/CborConformanceLevel.csUses HashCode.AddBytes unconditionally (polyfilled downlevel).
src/libraries/System.Formats.Cbor/src/System.Formats.Cbor.csprojLinks DateTimeOffset/HashCode/Array polyfills for non-.NETCoreApp.
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Base2ExponentialHistogramAggregator.csUses double.IsFinite directly; removes local helper.
src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csprojLinks DoublePolyfills.cs for non-.NETCoreApp.
src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csprojReplaces product-local polyfills include with linked Common polyfills in tests.
src/libraries/System.Collections.Immutable/src/System/Polyfills.csRemoves library-local polyfills file.
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenHashTable.csUses Array.MaxLength unconditionally.
src/libraries/System.Collections.Immutable/src/System.Collections.Immutable.csprojRemoves System\Polyfills.cs; links Common polyfills for non-.NETCoreApp.
src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.csSimplifies conditional IsAndroidOrAppleMobile expression body.
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csprojLinks TextWriter/Encoding polyfills for non-.NETCoreApp.
src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.csRemoves unsafe GetChars fallback in favor of polyfilled overloads; simplifies char handling.
src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiParsingLogConsole.csUses TextWriter.Write(ReadOnlySpan<char>) unconditionally (polyfilled downlevel).
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/SystemdHelpers.csUses Environment.ProcessId unconditionally (polyfilled downlevel).
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/Microsoft.Extensions.Hosting.Systemd.csprojLinks EnvironmentPolyfills.cs for non-.NETCoreApp.
src/libraries/Common/src/Polyfills/TextWriterPolyfills.csAdds downlevel TextWriter.Write(ReadOnlySpan<char>).
src/libraries/Common/src/Polyfills/StreamPolyfills.csAdds downlevel Stream.ReadExactly(byte[]) and CopyToAsync(Stream, CancellationToken).
src/libraries/Common/src/Polyfills/StopwatchPolyfills.csAdds downlevel Stopwatch.GetElapsedTime overloads.
src/libraries/Common/src/Polyfills/SinglePolyfills.csAdds downlevel float.IsFinite.
src/libraries/Common/src/Polyfills/DoublePolyfills.csAdds downlevel double.IsFinite.
src/libraries/Common/src/Polyfills/BitArrayPolyfills.csAdds downlevel BitArray.HasAllSet().
src/libraries/Common/src/Polyfills/ArrayPolyfills.csAdds downlevel Array.MaxLength.
src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.csAdds downlevel DateTimeOffset.TotalOffsetMinutes.
src/libraries/Common/src/Polyfills/HashCodePolyfills.csAdds downlevel HashCode.AddBytes(ReadOnlySpan<byte>) as an extension method.
src/libraries/Common/src/Polyfills/EnvironmentPolyfills.csAdds downlevel Environment.ProcessId.
src/libraries/Common/src/Polyfills/GuidPolyfills.csAdds downlevel Guid.TryWriteBytes(Span<byte>).
src/libraries/Common/src/Polyfills/BitConverterPolyfills.csAdds downlevel BitConverter.SingleToUInt32Bits / DoubleToUInt64Bits.
src/libraries/Common/src/Polyfills/IReadOnlySetPolyfill.csAdds downlevel IReadOnlySet<T> (guarded by #if !NET).
src/libraries/Common/src/Polyfills/BitOperationsPolyfill.csAdds downlevel System.Numerics.BitOperations.RotateLeft (guarded by #if !NET).
src/libraries/Common/src/Polyfills/CollectionsPolyfills.csAdds downlevel KeyValuePair.Deconstruct extension method.
src/libraries/Common/src/Polyfills/DictionaryPolyfills.csAdds downlevel TryAdd / TryDequeue polyfills (new shared source).

Comment threadsrc/libraries/Common/src/Polyfills/EnvironmentPolyfills.cs Outdated
@github-actions

This comment has been minimized.

Leverage polyfills already in Common/src/Polyfills/ to remove #if NET
blocks across additional libraries:
- ArrayPolyfills: LengthBuckets.cs, ArrayInfo.cs (Nrbf)
- IReadOnlySetPolyfill: ImmutableSortedSet, ImmutableHashSet, FrozenSet
- StreamPolyfills: SoundPlayer.cs, XmlBuffer.cs (Syndication)
- DateTimeOffsetPolyfills: CacheEntry.cs, Rss20FeedFormatter, Atom10FeedFormatter
- HashCodePolyfills: CoseHeaderValue.cs
- EnvironmentPolyfills: Win32.cs (Hosting.WindowsServices), SystemdHelpers.cs cleanup
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

CopilotAI review requested due to automatic review settings March 30, 2026 02:49
- EnvironmentPolyfills: dispose Process instance to avoid handle leak
- DoublePolyfills/SinglePolyfills: use same bit-manipulation impl as
the real Double.IsFinite/Single.IsFinite in CoreLib
- IReadOnlySetPolyfill: add comment explaining why #if !NET is needed
- StreamPolyfills: add comment explaining the 81_920 buffer size constant
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New polyfill: RuntimeHelpersPolyfills.cs provides:
- TryEnsureSufficientExecutionStack (wraps EnsureSufficientExecutionStack)
- GetUninitializedObject (delegates to FormatterServices)
Cleaned up in System.Text.Json:
- StackHelper.cs: remove #if NET for TryEnsureSufficientExecutionStack
- FSharpUnionConverter.cs: remove #if NET for GetUninitializedObject
and dead conditional using
Skipped (not polyfillable with extension() blocks):
- JsonArray.cs: List.AddRange(ReadOnlySpan) and uint.TryFormat
- CharConverter.cs: new ReadOnlySpan<char>(in value) constructor
- JsonCamelCaseNamingPolicy.cs: string.Create (shared with generators)
- JsonReaderHelper.Unescaping.cs: Utf8.IsValid (different error handling)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 66 out of 66 changed files in this pull request and generated 2 comments.

Comment threadsrc/libraries/System.Text.Json/src/System/Text/Json/StackHelper.cs Outdated
@github-actions

This comment has been minimized.

- Fix stray carriage return in Caching.Memory csproj
- JsonConsoleFormatter: use stackalloc span for char writing instead of
ToString() to avoid allocation regression (copilot review)
- Delete StackHelper.cs, inline RuntimeHelpers.TryEnsureSufficientExecutionStack
call directly in JsonElement.cs (jkotas)
- Revert IReadOnlySet polyfill: restore #if NET guards on FrozenSet,
ImmutableHashSet, ImmutableSortedSet; keep IReadOnlySet<T> definition
in library-local Polyfills.cs (jkotas concern about subtle bugs)
- Rename BitOperationsPolyfill.cs -> BitOperationsPolyfills.cs for
consistent plural naming across all polyfill files
- Remove dead StreamPolyfills ref from System.Windows.Extensions
(only targets NETCoreApp, polyfill would never compile)
- Fix EncodingPolyfills Link path for directory structure consistency
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

- Fully revert all changes to SimpleConsoleFormatter.cs
- JsonConsoleFormatter: use [charValue] collection literal instead of
stackalloc + manual assignment
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 30, 2026 14:36
@EgorBo
EgorBo removed the request for review from CopilotMarch 31, 2026 13:27

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Observation — EncodingPolyfills.cs lives outside Polyfills/ directory
EncodingPolyfills.cs is at $(CommonPath)System\Text\EncodingPolyfills.cs (pre-existing file not moved by this PR) but is l...

Done in 0f64157. Moved EncodingPolyfills.cs from src/libraries/Common/src/System/Text/ to src/libraries/Common/src/Polyfills/, updated the file to use a file-scoped namespace, and updated the references in both Directory.Build.targets and Microsoft.Extensions.Logging.Console.csproj.

CopilotAI changed the title Move pollyfills for !NET targets to src/libraries/Common/src/Polyfills/Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/Mar 31, 2026
CopilotAI requested a review from jkotasMarch 31, 2026 13:29
@EgorBo

EgorBo commented Mar 31, 2026

Copy link
Copy Markdown
MemberAuthor

Ah, I was not aware it finally is able to push into developers' PRs 🙂 . Although, I guess it's possible only because my local Copilot filed this PR in dotnet/runtime and not in EgorBo/runtime

@jkotas

Copy link
Copy Markdown
Member

Right, it works only for PRs in dotnet/runtime currently.

@jkotas

Copy link
Copy Markdown
Member

The polyfill creates and disposes a Process object on every access. This matches the pattern used by the old inline #else code and only runs on downlevel TFMs where Environment.ProcessId doesn't exist. On .NETCoreApp the real property is used instead (zero-cost)

@copilot Address this feedback by adding a simple cache

@EgorBo

Copy link
Copy Markdown
MemberAuthor

@copilot Address this feedback by adding a simple cache

I wasn't sure it in and left it as is. Actually, my impl is even better than whatever we had before - we didn't even dispose process objects.

CopilotAI review requested due to automatic review settings March 31, 2026 15:58
@EgorBo
EgorBo removed the request for review from CopilotMarch 31, 2026 15:58

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

The polyfill creates and disposes a Process object on every access. This matches the pattern used by the old inline #else code and only runs on downlevel TFMs where Environment.ProcessId doesn't exi...

Done in f0bf71e. Added a s_processId static field initialized to 0 (not a valid PID) as the cache sentinel — the Process object is now only created once per process lifetime.

CopilotAI requested a review from jkotasMarch 31, 2026 15:59
@jkotas

Copy link
Copy Markdown
Member

I wasn't sure it in and left it as is

I think we want to avoid bad surprises when using polyfills.

@EgorBo
EgorBo enabled auto-merge (squash) April 1, 2026 00:27
@EgorBo

Copy link
Copy Markdown
MemberAuthor

/ba-g deadletter (WasmTestOnV8)

@jkotas

Copy link
Copy Markdown
Member

I have asked copilot to look for more https://github.com/dotnet/runtime/tasks/8e893e7e-9ea0-4c23-9402-5d3b9a8a0af2

jkotas added a commit that referenced this pull request Apr 1, 2026
Follow-up to #126287 — consolidates library-local `BitOperations`,
`Stream`, `StringBuilder`, and `Stack` polyfills under
`src/libraries/Common/src/Polyfills/`.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 1, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@EgorBo@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/ - #126287

Merged
EgorBo merged 22 commits into
mainfrom
common-pollyfills
Apr 1, 2026
Merged

Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/#126287
EgorBo merged 22 commits into
mainfrom
common-pollyfills

Conversation

@EgorBo

@EgorBoEgorBo commented Mar 30, 2026

Copy link
Copy Markdown
Member

This allows us to pretend new APIs exist for .NET Standard 2.0/NETFX/etc targets and avoid #if NET else mess.

Description

  • Moved downlevel polyfills into src/libraries/Common/src/Polyfills/ and updated multiple libraries to call "modern" APIs unconditionally (e.g., Stopwatch.GetElapsedTime, double.IsFinite, Array.MaxLength), relying on polyfills for non-.NETCoreApp targets to reduce #if usage.
  • Replaced many #if NET / #else code paths with unified API calls and added corresponding polyfills via $(CommonPath)Polyfills\*.cs.
  • Removed library-local polyfill helpers (e.g., JsonHelpers.IsFinite, System.Collections.Immutable's System/Polyfills.cs) in favor of shared Common polyfills.
  • Updated project files to link in the required polyfill sources for non-.NETCoreApp target frameworks.
  • Moved src/libraries/Common/tests/System/Collections/DictionaryExtensions.cs into src/libraries/Common/src/Polyfills/DictionaryPolyfills.cs to consolidate duplicate TryAdd implementations.
  • Moved EncodingPolyfills.cs from src/libraries/Common/src/System/Text/ to src/libraries/Common/src/Polyfills/ for consistent directory structure; updated references in Directory.Build.targets and Microsoft.Extensions.Logging.Console.csproj.
  • Cached Environment.ProcessId in EnvironmentPolyfills using a static field to avoid creating and disposing a Process object on every access on downlevel TFMs.

EgorBoand others added 5 commits March 30, 2026 02:49
…e IsFinite
- Revert OperatingSystem.Is* polyfills (keep #if NET guards)
- Replace more 0x7FFFFFC7 constants with Array.MaxLength via polyfill
(FrozenHashTable 2nd occurrence, CborWriter, JsonDocument.MetadataDb)
- Inline JsonHelpers.IsFinite into call sites (double.IsFinite/float.IsFinite)
- Inline Base2ExponentialHistogramAggregator.IsFinite into call sites
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split into IReadOnlySetPolyfill.cs and BitOperationsPolyfill.cs.
Both use #if !NET guards since the types don't exist on downlevel.
Update both src and test csproj references.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split into BitConverterPolyfills.cs and GuidPolyfills.cs for reuse
across libraries.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 30, 2026 01:54
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-meta
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Moves downlevel polyfills into src/libraries/Common/src/Polyfills/ and updates multiple libraries to call the “modern” APIs unconditionally (e.g., Stopwatch.GetElapsedTime, double.IsFinite, Array.MaxLength), relying on polyfills for non-.NETCoreApp targets to reduce #if usage.

Changes:

  • Replaced many #if NET / #else code paths with unified API calls and added corresponding polyfills via $(CommonPath)Polyfills\*.cs.
  • Removed library-local polyfill helpers (e.g., JsonHelpers.IsFinite, System.Collections.Immutable’s System/Polyfills.cs) in favor of shared Common polyfills.
  • Updated project files to link in the required polyfill sources for non-.NETCoreApp target frameworks.

Reviewed changes

Copilot reviewed 45 out of 45 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.csUses Stopwatch.GetElapsedTime unconditionally (polyfilled for downlevel).
src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csprojLinks StopwatchPolyfills.cs for non-.NETCoreApp builds.
src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.csSwitches finite checks to double.IsFinite / float.IsFinite.
src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.csSwitches finite checks to double.IsFinite / float.IsFinite.
src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.csRemoves local IsFinite helpers and BitArray.HasAllSet polyfill.
src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.MetadataDb.csUses Array.MaxLength instead of hardcoded constant.
src/libraries/System.Text.Json/src/System.Text.Json.csprojLinks Double/Single/BitArray/Array polyfills for non-.NETCoreApp.
src/libraries/System.Speech/src/System.Speech.csprojLinks StreamPolyfills.cs for non-.NETCoreApp.
src/libraries/System.Speech/src/Internal/Synthesis/EngineSite.csReplaces manual read loop with Stream.ReadExactly.
src/libraries/System.Speech/src/Internal/Synthesis/AudioBase.csReplaces manual read loop with Stream.ReadExactly.
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csprojReplaces in-project polyfills with linked Common BitConverter/Guid polyfills.
src/libraries/System.IO.Hashing/src/System/IO/Hashing/NonCryptographicHashAlgorithm.csUses CopyToAsync(destination, cancellationToken) overload unconditionally (polyfilled downlevel).
src/libraries/System.IO.Hashing/src/System.IO.Hashing.csprojLinks StreamPolyfills.cs for non-.NETCoreApp.
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.csUses Array.MaxLength unconditionally.
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Tag.csUses DateTimeOffset.TotalOffsetMinutes unconditionally (polyfilled downlevel).
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/CborConformanceLevel.csUses HashCode.AddBytes unconditionally (polyfilled downlevel).
src/libraries/System.Formats.Cbor/src/System.Formats.Cbor.csprojLinks DateTimeOffset/HashCode/Array polyfills for non-.NETCoreApp.
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Base2ExponentialHistogramAggregator.csUses double.IsFinite directly; removes local helper.
src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csprojLinks DoublePolyfills.cs for non-.NETCoreApp.
src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csprojReplaces product-local polyfills include with linked Common polyfills in tests.
src/libraries/System.Collections.Immutable/src/System/Polyfills.csRemoves library-local polyfills file.
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenHashTable.csUses Array.MaxLength unconditionally.
src/libraries/System.Collections.Immutable/src/System.Collections.Immutable.csprojRemoves System\Polyfills.cs; links Common polyfills for non-.NETCoreApp.
src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.csSimplifies conditional IsAndroidOrAppleMobile expression body.
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csprojLinks TextWriter/Encoding polyfills for non-.NETCoreApp.
src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.csRemoves unsafe GetChars fallback in favor of polyfilled overloads; simplifies char handling.
src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiParsingLogConsole.csUses TextWriter.Write(ReadOnlySpan<char>) unconditionally (polyfilled downlevel).
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/SystemdHelpers.csUses Environment.ProcessId unconditionally (polyfilled downlevel).
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/Microsoft.Extensions.Hosting.Systemd.csprojLinks EnvironmentPolyfills.cs for non-.NETCoreApp.
src/libraries/Common/src/Polyfills/TextWriterPolyfills.csAdds downlevel TextWriter.Write(ReadOnlySpan<char>).
src/libraries/Common/src/Polyfills/StreamPolyfills.csAdds downlevel Stream.ReadExactly(byte[]) and CopyToAsync(Stream, CancellationToken).
src/libraries/Common/src/Polyfills/StopwatchPolyfills.csAdds downlevel Stopwatch.GetElapsedTime overloads.
src/libraries/Common/src/Polyfills/SinglePolyfills.csAdds downlevel float.IsFinite.
src/libraries/Common/src/Polyfills/DoublePolyfills.csAdds downlevel double.IsFinite.
src/libraries/Common/src/Polyfills/BitArrayPolyfills.csAdds downlevel BitArray.HasAllSet().
src/libraries/Common/src/Polyfills/ArrayPolyfills.csAdds downlevel Array.MaxLength.
src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.csAdds downlevel DateTimeOffset.TotalOffsetMinutes.
src/libraries/Common/src/Polyfills/HashCodePolyfills.csAdds downlevel HashCode.AddBytes(ReadOnlySpan<byte>) as an extension method.
src/libraries/Common/src/Polyfills/EnvironmentPolyfills.csAdds downlevel Environment.ProcessId.
src/libraries/Common/src/Polyfills/GuidPolyfills.csAdds downlevel Guid.TryWriteBytes(Span<byte>).
src/libraries/Common/src/Polyfills/BitConverterPolyfills.csAdds downlevel BitConverter.SingleToUInt32Bits / DoubleToUInt64Bits.
src/libraries/Common/src/Polyfills/IReadOnlySetPolyfill.csAdds downlevel IReadOnlySet<T> (guarded by #if !NET).
src/libraries/Common/src/Polyfills/BitOperationsPolyfill.csAdds downlevel System.Numerics.BitOperations.RotateLeft (guarded by #if !NET).
src/libraries/Common/src/Polyfills/CollectionsPolyfills.csAdds downlevel KeyValuePair.Deconstruct extension method.
src/libraries/Common/src/Polyfills/DictionaryPolyfills.csAdds downlevel TryAdd / TryDequeue polyfills (new shared source).

Comment threadsrc/libraries/Common/src/Polyfills/EnvironmentPolyfills.cs Outdated
@github-actions

This comment has been minimized.

Leverage polyfills already in Common/src/Polyfills/ to remove #if NET
blocks across additional libraries:
- ArrayPolyfills: LengthBuckets.cs, ArrayInfo.cs (Nrbf)
- IReadOnlySetPolyfill: ImmutableSortedSet, ImmutableHashSet, FrozenSet
- StreamPolyfills: SoundPlayer.cs, XmlBuffer.cs (Syndication)
- DateTimeOffsetPolyfills: CacheEntry.cs, Rss20FeedFormatter, Atom10FeedFormatter
- HashCodePolyfills: CoseHeaderValue.cs
- EnvironmentPolyfills: Win32.cs (Hosting.WindowsServices), SystemdHelpers.cs cleanup
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

CopilotAI review requested due to automatic review settings March 30, 2026 02:49
- EnvironmentPolyfills: dispose Process instance to avoid handle leak
- DoublePolyfills/SinglePolyfills: use same bit-manipulation impl as
the real Double.IsFinite/Single.IsFinite in CoreLib
- IReadOnlySetPolyfill: add comment explaining why #if !NET is needed
- StreamPolyfills: add comment explaining the 81_920 buffer size constant
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New polyfill: RuntimeHelpersPolyfills.cs provides:
- TryEnsureSufficientExecutionStack (wraps EnsureSufficientExecutionStack)
- GetUninitializedObject (delegates to FormatterServices)
Cleaned up in System.Text.Json:
- StackHelper.cs: remove #if NET for TryEnsureSufficientExecutionStack
- FSharpUnionConverter.cs: remove #if NET for GetUninitializedObject
and dead conditional using
Skipped (not polyfillable with extension() blocks):
- JsonArray.cs: List.AddRange(ReadOnlySpan) and uint.TryFormat
- CharConverter.cs: new ReadOnlySpan<char>(in value) constructor
- JsonCamelCaseNamingPolicy.cs: string.Create (shared with generators)
- JsonReaderHelper.Unescaping.cs: Utf8.IsValid (different error handling)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 66 out of 66 changed files in this pull request and generated 2 comments.

Comment threadsrc/libraries/System.Text.Json/src/System/Text/Json/StackHelper.cs Outdated
@github-actions

This comment has been minimized.

- Fix stray carriage return in Caching.Memory csproj
- JsonConsoleFormatter: use stackalloc span for char writing instead of
ToString() to avoid allocation regression (copilot review)
- Delete StackHelper.cs, inline RuntimeHelpers.TryEnsureSufficientExecutionStack
call directly in JsonElement.cs (jkotas)
- Revert IReadOnlySet polyfill: restore #if NET guards on FrozenSet,
ImmutableHashSet, ImmutableSortedSet; keep IReadOnlySet<T> definition
in library-local Polyfills.cs (jkotas concern about subtle bugs)
- Rename BitOperationsPolyfill.cs -> BitOperationsPolyfills.cs for
consistent plural naming across all polyfill files
- Remove dead StreamPolyfills ref from System.Windows.Extensions
(only targets NETCoreApp, polyfill would never compile)
- Fix EncodingPolyfills Link path for directory structure consistency
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

- Fully revert all changes to SimpleConsoleFormatter.cs
- JsonConsoleFormatter: use [charValue] collection literal instead of
stackalloc + manual assignment
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 30, 2026 14:36
@EgorBo
EgorBo removed the request for review from CopilotMarch 31, 2026 13:27

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Observation — EncodingPolyfills.cs lives outside Polyfills/ directory
EncodingPolyfills.cs is at $(CommonPath)System\Text\EncodingPolyfills.cs (pre-existing file not moved by this PR) but is l...

Done in 0f64157. Moved EncodingPolyfills.cs from src/libraries/Common/src/System/Text/ to src/libraries/Common/src/Polyfills/, updated the file to use a file-scoped namespace, and updated the references in both Directory.Build.targets and Microsoft.Extensions.Logging.Console.csproj.

CopilotAI changed the title Move pollyfills for !NET targets to src/libraries/Common/src/Polyfills/Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/Mar 31, 2026
CopilotAI requested a review from jkotasMarch 31, 2026 13:29
@EgorBo

EgorBo commented Mar 31, 2026

Copy link
Copy Markdown
MemberAuthor

Ah, I was not aware it finally is able to push into developers' PRs 🙂 . Although, I guess it's possible only because my local Copilot filed this PR in dotnet/runtime and not in EgorBo/runtime

@jkotas

Copy link
Copy Markdown
Member

Right, it works only for PRs in dotnet/runtime currently.

@jkotas

Copy link
Copy Markdown
Member

The polyfill creates and disposes a Process object on every access. This matches the pattern used by the old inline #else code and only runs on downlevel TFMs where Environment.ProcessId doesn't exist. On .NETCoreApp the real property is used instead (zero-cost)

@copilot Address this feedback by adding a simple cache

@EgorBo

Copy link
Copy Markdown
MemberAuthor

@copilot Address this feedback by adding a simple cache

I wasn't sure it in and left it as is. Actually, my impl is even better than whatever we had before - we didn't even dispose process objects.

CopilotAI review requested due to automatic review settings March 31, 2026 15:58
@EgorBo
EgorBo removed the request for review from CopilotMarch 31, 2026 15:58

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

The polyfill creates and disposes a Process object on every access. This matches the pattern used by the old inline #else code and only runs on downlevel TFMs where Environment.ProcessId doesn't exi...

Done in f0bf71e. Added a s_processId static field initialized to 0 (not a valid PID) as the cache sentinel — the Process object is now only created once per process lifetime.

CopilotAI requested a review from jkotasMarch 31, 2026 15:59
@jkotas

Copy link
Copy Markdown
Member

I wasn't sure it in and left it as is

I think we want to avoid bad surprises when using polyfills.

@EgorBo
EgorBo enabled auto-merge (squash) April 1, 2026 00:27
@EgorBo

Copy link
Copy Markdown
MemberAuthor

/ba-g deadletter (WasmTestOnV8)

@jkotas

Copy link
Copy Markdown
Member

I have asked copilot to look for more https://github.com/dotnet/runtime/tasks/8e893e7e-9ea0-4c23-9402-5d3b9a8a0af2

jkotas added a commit that referenced this pull request Apr 1, 2026
Follow-up to #126287 — consolidates library-local `BitOperations`,
`Stream`, `StringBuilder`, and `Stack` polyfills under
`src/libraries/Common/src/Polyfills/`.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 1, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@EgorBo@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/ - #126287

Merged
EgorBo merged 22 commits into
mainfrom
common-pollyfills
Apr 1, 2026
Merged

Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/#126287
EgorBo merged 22 commits into
mainfrom
common-pollyfills

Conversation

@EgorBo

@EgorBoEgorBo commented Mar 30, 2026

Copy link
Copy Markdown
Member

This allows us to pretend new APIs exist for .NET Standard 2.0/NETFX/etc targets and avoid #if NET else mess.

Description

  • Moved downlevel polyfills into src/libraries/Common/src/Polyfills/ and updated multiple libraries to call "modern" APIs unconditionally (e.g., Stopwatch.GetElapsedTime, double.IsFinite, Array.MaxLength), relying on polyfills for non-.NETCoreApp targets to reduce #if usage.
  • Replaced many #if NET / #else code paths with unified API calls and added corresponding polyfills via $(CommonPath)Polyfills\*.cs.
  • Removed library-local polyfill helpers (e.g., JsonHelpers.IsFinite, System.Collections.Immutable's System/Polyfills.cs) in favor of shared Common polyfills.
  • Updated project files to link in the required polyfill sources for non-.NETCoreApp target frameworks.
  • Moved src/libraries/Common/tests/System/Collections/DictionaryExtensions.cs into src/libraries/Common/src/Polyfills/DictionaryPolyfills.cs to consolidate duplicate TryAdd implementations.
  • Moved EncodingPolyfills.cs from src/libraries/Common/src/System/Text/ to src/libraries/Common/src/Polyfills/ for consistent directory structure; updated references in Directory.Build.targets and Microsoft.Extensions.Logging.Console.csproj.
  • Cached Environment.ProcessId in EnvironmentPolyfills using a static field to avoid creating and disposing a Process object on every access on downlevel TFMs.

EgorBoand others added 5 commits March 30, 2026 02:49
…e IsFinite
- Revert OperatingSystem.Is* polyfills (keep #if NET guards)
- Replace more 0x7FFFFFC7 constants with Array.MaxLength via polyfill
(FrozenHashTable 2nd occurrence, CborWriter, JsonDocument.MetadataDb)
- Inline JsonHelpers.IsFinite into call sites (double.IsFinite/float.IsFinite)
- Inline Base2ExponentialHistogramAggregator.IsFinite into call sites
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split into IReadOnlySetPolyfill.cs and BitOperationsPolyfill.cs.
Both use #if !NET guards since the types don't exist on downlevel.
Update both src and test csproj references.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split into BitConverterPolyfills.cs and GuidPolyfills.cs for reuse
across libraries.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 30, 2026 01:54
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-meta
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Moves downlevel polyfills into src/libraries/Common/src/Polyfills/ and updates multiple libraries to call the “modern” APIs unconditionally (e.g., Stopwatch.GetElapsedTime, double.IsFinite, Array.MaxLength), relying on polyfills for non-.NETCoreApp targets to reduce #if usage.

Changes:

  • Replaced many #if NET / #else code paths with unified API calls and added corresponding polyfills via $(CommonPath)Polyfills\*.cs.
  • Removed library-local polyfill helpers (e.g., JsonHelpers.IsFinite, System.Collections.Immutable’s System/Polyfills.cs) in favor of shared Common polyfills.
  • Updated project files to link in the required polyfill sources for non-.NETCoreApp target frameworks.

Reviewed changes

Copilot reviewed 45 out of 45 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.csUses Stopwatch.GetElapsedTime unconditionally (polyfilled for downlevel).
src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csprojLinks StopwatchPolyfills.cs for non-.NETCoreApp builds.
src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.csSwitches finite checks to double.IsFinite / float.IsFinite.
src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.csSwitches finite checks to double.IsFinite / float.IsFinite.
src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.csRemoves local IsFinite helpers and BitArray.HasAllSet polyfill.
src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.MetadataDb.csUses Array.MaxLength instead of hardcoded constant.
src/libraries/System.Text.Json/src/System.Text.Json.csprojLinks Double/Single/BitArray/Array polyfills for non-.NETCoreApp.
src/libraries/System.Speech/src/System.Speech.csprojLinks StreamPolyfills.cs for non-.NETCoreApp.
src/libraries/System.Speech/src/Internal/Synthesis/EngineSite.csReplaces manual read loop with Stream.ReadExactly.
src/libraries/System.Speech/src/Internal/Synthesis/AudioBase.csReplaces manual read loop with Stream.ReadExactly.
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csprojReplaces in-project polyfills with linked Common BitConverter/Guid polyfills.
src/libraries/System.IO.Hashing/src/System/IO/Hashing/NonCryptographicHashAlgorithm.csUses CopyToAsync(destination, cancellationToken) overload unconditionally (polyfilled downlevel).
src/libraries/System.IO.Hashing/src/System.IO.Hashing.csprojLinks StreamPolyfills.cs for non-.NETCoreApp.
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.csUses Array.MaxLength unconditionally.
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Tag.csUses DateTimeOffset.TotalOffsetMinutes unconditionally (polyfilled downlevel).
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/CborConformanceLevel.csUses HashCode.AddBytes unconditionally (polyfilled downlevel).
src/libraries/System.Formats.Cbor/src/System.Formats.Cbor.csprojLinks DateTimeOffset/HashCode/Array polyfills for non-.NETCoreApp.
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Base2ExponentialHistogramAggregator.csUses double.IsFinite directly; removes local helper.
src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csprojLinks DoublePolyfills.cs for non-.NETCoreApp.
src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csprojReplaces product-local polyfills include with linked Common polyfills in tests.
src/libraries/System.Collections.Immutable/src/System/Polyfills.csRemoves library-local polyfills file.
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenHashTable.csUses Array.MaxLength unconditionally.
src/libraries/System.Collections.Immutable/src/System.Collections.Immutable.csprojRemoves System\Polyfills.cs; links Common polyfills for non-.NETCoreApp.
src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.csSimplifies conditional IsAndroidOrAppleMobile expression body.
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csprojLinks TextWriter/Encoding polyfills for non-.NETCoreApp.
src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.csRemoves unsafe GetChars fallback in favor of polyfilled overloads; simplifies char handling.
src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiParsingLogConsole.csUses TextWriter.Write(ReadOnlySpan<char>) unconditionally (polyfilled downlevel).
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/SystemdHelpers.csUses Environment.ProcessId unconditionally (polyfilled downlevel).
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/Microsoft.Extensions.Hosting.Systemd.csprojLinks EnvironmentPolyfills.cs for non-.NETCoreApp.
src/libraries/Common/src/Polyfills/TextWriterPolyfills.csAdds downlevel TextWriter.Write(ReadOnlySpan<char>).
src/libraries/Common/src/Polyfills/StreamPolyfills.csAdds downlevel Stream.ReadExactly(byte[]) and CopyToAsync(Stream, CancellationToken).
src/libraries/Common/src/Polyfills/StopwatchPolyfills.csAdds downlevel Stopwatch.GetElapsedTime overloads.
src/libraries/Common/src/Polyfills/SinglePolyfills.csAdds downlevel float.IsFinite.
src/libraries/Common/src/Polyfills/DoublePolyfills.csAdds downlevel double.IsFinite.
src/libraries/Common/src/Polyfills/BitArrayPolyfills.csAdds downlevel BitArray.HasAllSet().
src/libraries/Common/src/Polyfills/ArrayPolyfills.csAdds downlevel Array.MaxLength.
src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.csAdds downlevel DateTimeOffset.TotalOffsetMinutes.
src/libraries/Common/src/Polyfills/HashCodePolyfills.csAdds downlevel HashCode.AddBytes(ReadOnlySpan<byte>) as an extension method.
src/libraries/Common/src/Polyfills/EnvironmentPolyfills.csAdds downlevel Environment.ProcessId.
src/libraries/Common/src/Polyfills/GuidPolyfills.csAdds downlevel Guid.TryWriteBytes(Span<byte>).
src/libraries/Common/src/Polyfills/BitConverterPolyfills.csAdds downlevel BitConverter.SingleToUInt32Bits / DoubleToUInt64Bits.
src/libraries/Common/src/Polyfills/IReadOnlySetPolyfill.csAdds downlevel IReadOnlySet<T> (guarded by #if !NET).
src/libraries/Common/src/Polyfills/BitOperationsPolyfill.csAdds downlevel System.Numerics.BitOperations.RotateLeft (guarded by #if !NET).
src/libraries/Common/src/Polyfills/CollectionsPolyfills.csAdds downlevel KeyValuePair.Deconstruct extension method.
src/libraries/Common/src/Polyfills/DictionaryPolyfills.csAdds downlevel TryAdd / TryDequeue polyfills (new shared source).

Comment threadsrc/libraries/Common/src/Polyfills/EnvironmentPolyfills.cs Outdated
@github-actions

This comment has been minimized.

Leverage polyfills already in Common/src/Polyfills/ to remove #if NET
blocks across additional libraries:
- ArrayPolyfills: LengthBuckets.cs, ArrayInfo.cs (Nrbf)
- IReadOnlySetPolyfill: ImmutableSortedSet, ImmutableHashSet, FrozenSet
- StreamPolyfills: SoundPlayer.cs, XmlBuffer.cs (Syndication)
- DateTimeOffsetPolyfills: CacheEntry.cs, Rss20FeedFormatter, Atom10FeedFormatter
- HashCodePolyfills: CoseHeaderValue.cs
- EnvironmentPolyfills: Win32.cs (Hosting.WindowsServices), SystemdHelpers.cs cleanup
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

CopilotAI review requested due to automatic review settings March 30, 2026 02:49
- EnvironmentPolyfills: dispose Process instance to avoid handle leak
- DoublePolyfills/SinglePolyfills: use same bit-manipulation impl as
the real Double.IsFinite/Single.IsFinite in CoreLib
- IReadOnlySetPolyfill: add comment explaining why #if !NET is needed
- StreamPolyfills: add comment explaining the 81_920 buffer size constant
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New polyfill: RuntimeHelpersPolyfills.cs provides:
- TryEnsureSufficientExecutionStack (wraps EnsureSufficientExecutionStack)
- GetUninitializedObject (delegates to FormatterServices)
Cleaned up in System.Text.Json:
- StackHelper.cs: remove #if NET for TryEnsureSufficientExecutionStack
- FSharpUnionConverter.cs: remove #if NET for GetUninitializedObject
and dead conditional using
Skipped (not polyfillable with extension() blocks):
- JsonArray.cs: List.AddRange(ReadOnlySpan) and uint.TryFormat
- CharConverter.cs: new ReadOnlySpan<char>(in value) constructor
- JsonCamelCaseNamingPolicy.cs: string.Create (shared with generators)
- JsonReaderHelper.Unescaping.cs: Utf8.IsValid (different error handling)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 66 out of 66 changed files in this pull request and generated 2 comments.

Comment threadsrc/libraries/System.Text.Json/src/System/Text/Json/StackHelper.cs Outdated
@github-actions

This comment has been minimized.

- Fix stray carriage return in Caching.Memory csproj
- JsonConsoleFormatter: use stackalloc span for char writing instead of
ToString() to avoid allocation regression (copilot review)
- Delete StackHelper.cs, inline RuntimeHelpers.TryEnsureSufficientExecutionStack
call directly in JsonElement.cs (jkotas)
- Revert IReadOnlySet polyfill: restore #if NET guards on FrozenSet,
ImmutableHashSet, ImmutableSortedSet; keep IReadOnlySet<T> definition
in library-local Polyfills.cs (jkotas concern about subtle bugs)
- Rename BitOperationsPolyfill.cs -> BitOperationsPolyfills.cs for
consistent plural naming across all polyfill files
- Remove dead StreamPolyfills ref from System.Windows.Extensions
(only targets NETCoreApp, polyfill would never compile)
- Fix EncodingPolyfills Link path for directory structure consistency
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

- Fully revert all changes to SimpleConsoleFormatter.cs
- JsonConsoleFormatter: use [charValue] collection literal instead of
stackalloc + manual assignment
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 30, 2026 14:36
@EgorBo
EgorBo removed the request for review from CopilotMarch 31, 2026 13:27

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Observation — EncodingPolyfills.cs lives outside Polyfills/ directory
EncodingPolyfills.cs is at $(CommonPath)System\Text\EncodingPolyfills.cs (pre-existing file not moved by this PR) but is l...

Done in 0f64157. Moved EncodingPolyfills.cs from src/libraries/Common/src/System/Text/ to src/libraries/Common/src/Polyfills/, updated the file to use a file-scoped namespace, and updated the references in both Directory.Build.targets and Microsoft.Extensions.Logging.Console.csproj.

CopilotAI changed the title Move pollyfills for !NET targets to src/libraries/Common/src/Polyfills/Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/Mar 31, 2026
CopilotAI requested a review from jkotasMarch 31, 2026 13:29
@EgorBo

EgorBo commented Mar 31, 2026

Copy link
Copy Markdown
MemberAuthor

Ah, I was not aware it finally is able to push into developers' PRs 🙂 . Although, I guess it's possible only because my local Copilot filed this PR in dotnet/runtime and not in EgorBo/runtime

@jkotas

Copy link
Copy Markdown
Member

Right, it works only for PRs in dotnet/runtime currently.

@jkotas

Copy link
Copy Markdown
Member

The polyfill creates and disposes a Process object on every access. This matches the pattern used by the old inline #else code and only runs on downlevel TFMs where Environment.ProcessId doesn't exist. On .NETCoreApp the real property is used instead (zero-cost)

@copilot Address this feedback by adding a simple cache

@EgorBo

Copy link
Copy Markdown
MemberAuthor

@copilot Address this feedback by adding a simple cache

I wasn't sure it in and left it as is. Actually, my impl is even better than whatever we had before - we didn't even dispose process objects.

CopilotAI review requested due to automatic review settings March 31, 2026 15:58
@EgorBo
EgorBo removed the request for review from CopilotMarch 31, 2026 15:58

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

The polyfill creates and disposes a Process object on every access. This matches the pattern used by the old inline #else code and only runs on downlevel TFMs where Environment.ProcessId doesn't exi...

Done in f0bf71e. Added a s_processId static field initialized to 0 (not a valid PID) as the cache sentinel — the Process object is now only created once per process lifetime.

CopilotAI requested a review from jkotasMarch 31, 2026 15:59
@jkotas

Copy link
Copy Markdown
Member

I wasn't sure it in and left it as is

I think we want to avoid bad surprises when using polyfills.

@EgorBo
EgorBo enabled auto-merge (squash) April 1, 2026 00:27
@EgorBo

Copy link
Copy Markdown
MemberAuthor

/ba-g deadletter (WasmTestOnV8)

@jkotas

Copy link
Copy Markdown
Member

I have asked copilot to look for more https://github.com/dotnet/runtime/tasks/8e893e7e-9ea0-4c23-9402-5d3b9a8a0af2

jkotas added a commit that referenced this pull request Apr 1, 2026
Follow-up to #126287 — consolidates library-local `BitOperations`,
`Stream`, `StringBuilder`, and `Stack` polyfills under
`src/libraries/Common/src/Polyfills/`.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 1, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@EgorBo@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/ - #126287

Merged
EgorBo merged 22 commits into
mainfrom
common-pollyfills
Apr 1, 2026
Merged

Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/#126287
EgorBo merged 22 commits into
mainfrom
common-pollyfills

Conversation

@EgorBo

@EgorBoEgorBo commented Mar 30, 2026

Copy link
Copy Markdown
Member

This allows us to pretend new APIs exist for .NET Standard 2.0/NETFX/etc targets and avoid #if NET else mess.

Description

  • Moved downlevel polyfills into src/libraries/Common/src/Polyfills/ and updated multiple libraries to call "modern" APIs unconditionally (e.g., Stopwatch.GetElapsedTime, double.IsFinite, Array.MaxLength), relying on polyfills for non-.NETCoreApp targets to reduce #if usage.
  • Replaced many #if NET / #else code paths with unified API calls and added corresponding polyfills via $(CommonPath)Polyfills\*.cs.
  • Removed library-local polyfill helpers (e.g., JsonHelpers.IsFinite, System.Collections.Immutable's System/Polyfills.cs) in favor of shared Common polyfills.
  • Updated project files to link in the required polyfill sources for non-.NETCoreApp target frameworks.
  • Moved src/libraries/Common/tests/System/Collections/DictionaryExtensions.cs into src/libraries/Common/src/Polyfills/DictionaryPolyfills.cs to consolidate duplicate TryAdd implementations.
  • Moved EncodingPolyfills.cs from src/libraries/Common/src/System/Text/ to src/libraries/Common/src/Polyfills/ for consistent directory structure; updated references in Directory.Build.targets and Microsoft.Extensions.Logging.Console.csproj.
  • Cached Environment.ProcessId in EnvironmentPolyfills using a static field to avoid creating and disposing a Process object on every access on downlevel TFMs.

EgorBoand others added 5 commits March 30, 2026 02:49
…e IsFinite
- Revert OperatingSystem.Is* polyfills (keep #if NET guards)
- Replace more 0x7FFFFFC7 constants with Array.MaxLength via polyfill
(FrozenHashTable 2nd occurrence, CborWriter, JsonDocument.MetadataDb)
- Inline JsonHelpers.IsFinite into call sites (double.IsFinite/float.IsFinite)
- Inline Base2ExponentialHistogramAggregator.IsFinite into call sites
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split into IReadOnlySetPolyfill.cs and BitOperationsPolyfill.cs.
Both use #if !NET guards since the types don't exist on downlevel.
Update both src and test csproj references.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split into BitConverterPolyfills.cs and GuidPolyfills.cs for reuse
across libraries.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 30, 2026 01:54
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-meta
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Moves downlevel polyfills into src/libraries/Common/src/Polyfills/ and updates multiple libraries to call the “modern” APIs unconditionally (e.g., Stopwatch.GetElapsedTime, double.IsFinite, Array.MaxLength), relying on polyfills for non-.NETCoreApp targets to reduce #if usage.

Changes:

  • Replaced many #if NET / #else code paths with unified API calls and added corresponding polyfills via $(CommonPath)Polyfills\*.cs.
  • Removed library-local polyfill helpers (e.g., JsonHelpers.IsFinite, System.Collections.Immutable’s System/Polyfills.cs) in favor of shared Common polyfills.
  • Updated project files to link in the required polyfill sources for non-.NETCoreApp target frameworks.

Reviewed changes

Copilot reviewed 45 out of 45 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.csUses Stopwatch.GetElapsedTime unconditionally (polyfilled for downlevel).
src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csprojLinks StopwatchPolyfills.cs for non-.NETCoreApp builds.
src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.csSwitches finite checks to double.IsFinite / float.IsFinite.
src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.csSwitches finite checks to double.IsFinite / float.IsFinite.
src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.csRemoves local IsFinite helpers and BitArray.HasAllSet polyfill.
src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.MetadataDb.csUses Array.MaxLength instead of hardcoded constant.
src/libraries/System.Text.Json/src/System.Text.Json.csprojLinks Double/Single/BitArray/Array polyfills for non-.NETCoreApp.
src/libraries/System.Speech/src/System.Speech.csprojLinks StreamPolyfills.cs for non-.NETCoreApp.
src/libraries/System.Speech/src/Internal/Synthesis/EngineSite.csReplaces manual read loop with Stream.ReadExactly.
src/libraries/System.Speech/src/Internal/Synthesis/AudioBase.csReplaces manual read loop with Stream.ReadExactly.
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csprojReplaces in-project polyfills with linked Common BitConverter/Guid polyfills.
src/libraries/System.IO.Hashing/src/System/IO/Hashing/NonCryptographicHashAlgorithm.csUses CopyToAsync(destination, cancellationToken) overload unconditionally (polyfilled downlevel).
src/libraries/System.IO.Hashing/src/System.IO.Hashing.csprojLinks StreamPolyfills.cs for non-.NETCoreApp.
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.csUses Array.MaxLength unconditionally.
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Tag.csUses DateTimeOffset.TotalOffsetMinutes unconditionally (polyfilled downlevel).
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/CborConformanceLevel.csUses HashCode.AddBytes unconditionally (polyfilled downlevel).
src/libraries/System.Formats.Cbor/src/System.Formats.Cbor.csprojLinks DateTimeOffset/HashCode/Array polyfills for non-.NETCoreApp.
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Base2ExponentialHistogramAggregator.csUses double.IsFinite directly; removes local helper.
src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csprojLinks DoublePolyfills.cs for non-.NETCoreApp.
src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csprojReplaces product-local polyfills include with linked Common polyfills in tests.
src/libraries/System.Collections.Immutable/src/System/Polyfills.csRemoves library-local polyfills file.
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenHashTable.csUses Array.MaxLength unconditionally.
src/libraries/System.Collections.Immutable/src/System.Collections.Immutable.csprojRemoves System\Polyfills.cs; links Common polyfills for non-.NETCoreApp.
src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.csSimplifies conditional IsAndroidOrAppleMobile expression body.
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csprojLinks TextWriter/Encoding polyfills for non-.NETCoreApp.
src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.csRemoves unsafe GetChars fallback in favor of polyfilled overloads; simplifies char handling.
src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiParsingLogConsole.csUses TextWriter.Write(ReadOnlySpan<char>) unconditionally (polyfilled downlevel).
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/SystemdHelpers.csUses Environment.ProcessId unconditionally (polyfilled downlevel).
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/Microsoft.Extensions.Hosting.Systemd.csprojLinks EnvironmentPolyfills.cs for non-.NETCoreApp.
src/libraries/Common/src/Polyfills/TextWriterPolyfills.csAdds downlevel TextWriter.Write(ReadOnlySpan<char>).
src/libraries/Common/src/Polyfills/StreamPolyfills.csAdds downlevel Stream.ReadExactly(byte[]) and CopyToAsync(Stream, CancellationToken).
src/libraries/Common/src/Polyfills/StopwatchPolyfills.csAdds downlevel Stopwatch.GetElapsedTime overloads.
src/libraries/Common/src/Polyfills/SinglePolyfills.csAdds downlevel float.IsFinite.
src/libraries/Common/src/Polyfills/DoublePolyfills.csAdds downlevel double.IsFinite.
src/libraries/Common/src/Polyfills/BitArrayPolyfills.csAdds downlevel BitArray.HasAllSet().
src/libraries/Common/src/Polyfills/ArrayPolyfills.csAdds downlevel Array.MaxLength.
src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.csAdds downlevel DateTimeOffset.TotalOffsetMinutes.
src/libraries/Common/src/Polyfills/HashCodePolyfills.csAdds downlevel HashCode.AddBytes(ReadOnlySpan<byte>) as an extension method.
src/libraries/Common/src/Polyfills/EnvironmentPolyfills.csAdds downlevel Environment.ProcessId.
src/libraries/Common/src/Polyfills/GuidPolyfills.csAdds downlevel Guid.TryWriteBytes(Span<byte>).
src/libraries/Common/src/Polyfills/BitConverterPolyfills.csAdds downlevel BitConverter.SingleToUInt32Bits / DoubleToUInt64Bits.
src/libraries/Common/src/Polyfills/IReadOnlySetPolyfill.csAdds downlevel IReadOnlySet<T> (guarded by #if !NET).
src/libraries/Common/src/Polyfills/BitOperationsPolyfill.csAdds downlevel System.Numerics.BitOperations.RotateLeft (guarded by #if !NET).
src/libraries/Common/src/Polyfills/CollectionsPolyfills.csAdds downlevel KeyValuePair.Deconstruct extension method.
src/libraries/Common/src/Polyfills/DictionaryPolyfills.csAdds downlevel TryAdd / TryDequeue polyfills (new shared source).

Comment threadsrc/libraries/Common/src/Polyfills/EnvironmentPolyfills.cs Outdated
@github-actions

This comment has been minimized.

Leverage polyfills already in Common/src/Polyfills/ to remove #if NET
blocks across additional libraries:
- ArrayPolyfills: LengthBuckets.cs, ArrayInfo.cs (Nrbf)
- IReadOnlySetPolyfill: ImmutableSortedSet, ImmutableHashSet, FrozenSet
- StreamPolyfills: SoundPlayer.cs, XmlBuffer.cs (Syndication)
- DateTimeOffsetPolyfills: CacheEntry.cs, Rss20FeedFormatter, Atom10FeedFormatter
- HashCodePolyfills: CoseHeaderValue.cs
- EnvironmentPolyfills: Win32.cs (Hosting.WindowsServices), SystemdHelpers.cs cleanup
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

CopilotAI review requested due to automatic review settings March 30, 2026 02:49
- EnvironmentPolyfills: dispose Process instance to avoid handle leak
- DoublePolyfills/SinglePolyfills: use same bit-manipulation impl as
the real Double.IsFinite/Single.IsFinite in CoreLib
- IReadOnlySetPolyfill: add comment explaining why #if !NET is needed
- StreamPolyfills: add comment explaining the 81_920 buffer size constant
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New polyfill: RuntimeHelpersPolyfills.cs provides:
- TryEnsureSufficientExecutionStack (wraps EnsureSufficientExecutionStack)
- GetUninitializedObject (delegates to FormatterServices)
Cleaned up in System.Text.Json:
- StackHelper.cs: remove #if NET for TryEnsureSufficientExecutionStack
- FSharpUnionConverter.cs: remove #if NET for GetUninitializedObject
and dead conditional using
Skipped (not polyfillable with extension() blocks):
- JsonArray.cs: List.AddRange(ReadOnlySpan) and uint.TryFormat
- CharConverter.cs: new ReadOnlySpan<char>(in value) constructor
- JsonCamelCaseNamingPolicy.cs: string.Create (shared with generators)
- JsonReaderHelper.Unescaping.cs: Utf8.IsValid (different error handling)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 66 out of 66 changed files in this pull request and generated 2 comments.

Comment threadsrc/libraries/System.Text.Json/src/System/Text/Json/StackHelper.cs Outdated
@github-actions

This comment has been minimized.

- Fix stray carriage return in Caching.Memory csproj
- JsonConsoleFormatter: use stackalloc span for char writing instead of
ToString() to avoid allocation regression (copilot review)
- Delete StackHelper.cs, inline RuntimeHelpers.TryEnsureSufficientExecutionStack
call directly in JsonElement.cs (jkotas)
- Revert IReadOnlySet polyfill: restore #if NET guards on FrozenSet,
ImmutableHashSet, ImmutableSortedSet; keep IReadOnlySet<T> definition
in library-local Polyfills.cs (jkotas concern about subtle bugs)
- Rename BitOperationsPolyfill.cs -> BitOperationsPolyfills.cs for
consistent plural naming across all polyfill files
- Remove dead StreamPolyfills ref from System.Windows.Extensions
(only targets NETCoreApp, polyfill would never compile)
- Fix EncodingPolyfills Link path for directory structure consistency
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

- Fully revert all changes to SimpleConsoleFormatter.cs
- JsonConsoleFormatter: use [charValue] collection literal instead of
stackalloc + manual assignment
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 30, 2026 14:36
@EgorBo
EgorBo removed the request for review from CopilotMarch 31, 2026 13:27

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Observation — EncodingPolyfills.cs lives outside Polyfills/ directory
EncodingPolyfills.cs is at $(CommonPath)System\Text\EncodingPolyfills.cs (pre-existing file not moved by this PR) but is l...

Done in 0f64157. Moved EncodingPolyfills.cs from src/libraries/Common/src/System/Text/ to src/libraries/Common/src/Polyfills/, updated the file to use a file-scoped namespace, and updated the references in both Directory.Build.targets and Microsoft.Extensions.Logging.Console.csproj.

CopilotAI changed the title Move pollyfills for !NET targets to src/libraries/Common/src/Polyfills/Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/Mar 31, 2026
CopilotAI requested a review from jkotasMarch 31, 2026 13:29
@EgorBo

EgorBo commented Mar 31, 2026

Copy link
Copy Markdown
MemberAuthor

Ah, I was not aware it finally is able to push into developers' PRs 🙂 . Although, I guess it's possible only because my local Copilot filed this PR in dotnet/runtime and not in EgorBo/runtime

@jkotas

Copy link
Copy Markdown
Member

Right, it works only for PRs in dotnet/runtime currently.

@jkotas

Copy link
Copy Markdown
Member

The polyfill creates and disposes a Process object on every access. This matches the pattern used by the old inline #else code and only runs on downlevel TFMs where Environment.ProcessId doesn't exist. On .NETCoreApp the real property is used instead (zero-cost)

@copilot Address this feedback by adding a simple cache

@EgorBo

Copy link
Copy Markdown
MemberAuthor

@copilot Address this feedback by adding a simple cache

I wasn't sure it in and left it as is. Actually, my impl is even better than whatever we had before - we didn't even dispose process objects.

CopilotAI review requested due to automatic review settings March 31, 2026 15:58
@EgorBo
EgorBo removed the request for review from CopilotMarch 31, 2026 15:58

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

The polyfill creates and disposes a Process object on every access. This matches the pattern used by the old inline #else code and only runs on downlevel TFMs where Environment.ProcessId doesn't exi...

Done in f0bf71e. Added a s_processId static field initialized to 0 (not a valid PID) as the cache sentinel — the Process object is now only created once per process lifetime.

CopilotAI requested a review from jkotasMarch 31, 2026 15:59
@jkotas

Copy link
Copy Markdown
Member

I wasn't sure it in and left it as is

I think we want to avoid bad surprises when using polyfills.

@EgorBo
EgorBo enabled auto-merge (squash) April 1, 2026 00:27
@EgorBo

Copy link
Copy Markdown
MemberAuthor

/ba-g deadletter (WasmTestOnV8)

@jkotas

Copy link
Copy Markdown
Member

I have asked copilot to look for more https://github.com/dotnet/runtime/tasks/8e893e7e-9ea0-4c23-9402-5d3b9a8a0af2

jkotas added a commit that referenced this pull request Apr 1, 2026
Follow-up to #126287 — consolidates library-local `BitOperations`,
`Stream`, `StringBuilder`, and `Stack` polyfills under
`src/libraries/Common/src/Polyfills/`.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 1, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@EgorBo@jkotas
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/ - #126287

Merged
EgorBo merged 22 commits into
mainfrom
common-pollyfills
Apr 1, 2026
Merged

Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/#126287
EgorBo merged 22 commits into
mainfrom
common-pollyfills

Conversation

@EgorBo

@EgorBoEgorBo commented Mar 30, 2026

Copy link
Copy Markdown
Member

This allows us to pretend new APIs exist for .NET Standard 2.0/NETFX/etc targets and avoid #if NET else mess.

Description

  • Moved downlevel polyfills into src/libraries/Common/src/Polyfills/ and updated multiple libraries to call "modern" APIs unconditionally (e.g., Stopwatch.GetElapsedTime, double.IsFinite, Array.MaxLength), relying on polyfills for non-.NETCoreApp targets to reduce #if usage.
  • Replaced many #if NET / #else code paths with unified API calls and added corresponding polyfills via $(CommonPath)Polyfills\*.cs.
  • Removed library-local polyfill helpers (e.g., JsonHelpers.IsFinite, System.Collections.Immutable's System/Polyfills.cs) in favor of shared Common polyfills.
  • Updated project files to link in the required polyfill sources for non-.NETCoreApp target frameworks.
  • Moved src/libraries/Common/tests/System/Collections/DictionaryExtensions.cs into src/libraries/Common/src/Polyfills/DictionaryPolyfills.cs to consolidate duplicate TryAdd implementations.
  • Moved EncodingPolyfills.cs from src/libraries/Common/src/System/Text/ to src/libraries/Common/src/Polyfills/ for consistent directory structure; updated references in Directory.Build.targets and Microsoft.Extensions.Logging.Console.csproj.
  • Cached Environment.ProcessId in EnvironmentPolyfills using a static field to avoid creating and disposing a Process object on every access on downlevel TFMs.

EgorBoand others added 5 commits March 30, 2026 02:49
…e IsFinite
- Revert OperatingSystem.Is* polyfills (keep #if NET guards)
- Replace more 0x7FFFFFC7 constants with Array.MaxLength via polyfill
(FrozenHashTable 2nd occurrence, CborWriter, JsonDocument.MetadataDb)
- Inline JsonHelpers.IsFinite into call sites (double.IsFinite/float.IsFinite)
- Inline Base2ExponentialHistogramAggregator.IsFinite into call sites
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split into IReadOnlySetPolyfill.cs and BitOperationsPolyfill.cs.
Both use #if !NET guards since the types don't exist on downlevel.
Update both src and test csproj references.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split into BitConverterPolyfills.cs and GuidPolyfills.cs for reuse
across libraries.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 30, 2026 01:54
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-meta
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Moves downlevel polyfills into src/libraries/Common/src/Polyfills/ and updates multiple libraries to call the “modern” APIs unconditionally (e.g., Stopwatch.GetElapsedTime, double.IsFinite, Array.MaxLength), relying on polyfills for non-.NETCoreApp targets to reduce #if usage.

Changes:

  • Replaced many #if NET / #else code paths with unified API calls and added corresponding polyfills via $(CommonPath)Polyfills\*.cs.
  • Removed library-local polyfill helpers (e.g., JsonHelpers.IsFinite, System.Collections.Immutable’s System/Polyfills.cs) in favor of shared Common polyfills.
  • Updated project files to link in the required polyfill sources for non-.NETCoreApp target frameworks.

Reviewed changes

Copilot reviewed 45 out of 45 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
src/libraries/System.Threading.RateLimiting/src/System/Threading/RateLimiting/RateLimiterHelper.csUses Stopwatch.GetElapsedTime unconditionally (polyfilled for downlevel).
src/libraries/System.Threading.RateLimiting/src/System.Threading.RateLimiting.csprojLinks StopwatchPolyfills.cs for non-.NETCoreApp builds.
src/libraries/System.Text.Json/src/System/Text/Json/Writer/JsonWriterHelper.csSwitches finite checks to double.IsFinite / float.IsFinite.
src/libraries/System.Text.Json/src/System/Text/Json/Reader/Utf8JsonReader.TryGet.csSwitches finite checks to double.IsFinite / float.IsFinite.
src/libraries/System.Text.Json/src/System/Text/Json/JsonHelpers.csRemoves local IsFinite helpers and BitArray.HasAllSet polyfill.
src/libraries/System.Text.Json/src/System/Text/Json/Document/JsonDocument.MetadataDb.csUses Array.MaxLength instead of hardcoded constant.
src/libraries/System.Text.Json/src/System.Text.Json.csprojLinks Double/Single/BitArray/Array polyfills for non-.NETCoreApp.
src/libraries/System.Speech/src/System.Speech.csprojLinks StreamPolyfills.cs for non-.NETCoreApp.
src/libraries/System.Speech/src/Internal/Synthesis/EngineSite.csReplaces manual read loop with Stream.ReadExactly.
src/libraries/System.Speech/src/Internal/Synthesis/AudioBase.csReplaces manual read loop with Stream.ReadExactly.
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csprojReplaces in-project polyfills with linked Common BitConverter/Guid polyfills.
src/libraries/System.IO.Hashing/src/System/IO/Hashing/NonCryptographicHashAlgorithm.csUses CopyToAsync(destination, cancellationToken) overload unconditionally (polyfilled downlevel).
src/libraries/System.IO.Hashing/src/System.IO.Hashing.csprojLinks StreamPolyfills.cs for non-.NETCoreApp.
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.csUses Array.MaxLength unconditionally.
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/Writer/CborWriter.Tag.csUses DateTimeOffset.TotalOffsetMinutes unconditionally (polyfilled downlevel).
src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/CborConformanceLevel.csUses HashCode.AddBytes unconditionally (polyfilled downlevel).
src/libraries/System.Formats.Cbor/src/System.Formats.Cbor.csprojLinks DateTimeOffset/HashCode/Array polyfills for non-.NETCoreApp.
src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Base2ExponentialHistogramAggregator.csUses double.IsFinite directly; removes local helper.
src/libraries/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csprojLinks DoublePolyfills.cs for non-.NETCoreApp.
src/libraries/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csprojReplaces product-local polyfills include with linked Common polyfills in tests.
src/libraries/System.Collections.Immutable/src/System/Polyfills.csRemoves library-local polyfills file.
src/libraries/System.Collections.Immutable/src/System/Collections/Frozen/FrozenHashTable.csUses Array.MaxLength unconditionally.
src/libraries/System.Collections.Immutable/src/System.Collections.Immutable.csprojRemoves System\Polyfills.cs; links Common polyfills for non-.NETCoreApp.
src/libraries/Microsoft.Extensions.Logging.Console/src/SimpleConsoleFormatter.csSimplifies conditional IsAndroidOrAppleMobile expression body.
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csprojLinks TextWriter/Encoding polyfills for non-.NETCoreApp.
src/libraries/Microsoft.Extensions.Logging.Console/src/JsonConsoleFormatter.csRemoves unsafe GetChars fallback in favor of polyfilled overloads; simplifies char handling.
src/libraries/Microsoft.Extensions.Logging.Console/src/AnsiParsingLogConsole.csUses TextWriter.Write(ReadOnlySpan<char>) unconditionally (polyfilled downlevel).
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/SystemdHelpers.csUses Environment.ProcessId unconditionally (polyfilled downlevel).
src/libraries/Microsoft.Extensions.Hosting.Systemd/src/Microsoft.Extensions.Hosting.Systemd.csprojLinks EnvironmentPolyfills.cs for non-.NETCoreApp.
src/libraries/Common/src/Polyfills/TextWriterPolyfills.csAdds downlevel TextWriter.Write(ReadOnlySpan<char>).
src/libraries/Common/src/Polyfills/StreamPolyfills.csAdds downlevel Stream.ReadExactly(byte[]) and CopyToAsync(Stream, CancellationToken).
src/libraries/Common/src/Polyfills/StopwatchPolyfills.csAdds downlevel Stopwatch.GetElapsedTime overloads.
src/libraries/Common/src/Polyfills/SinglePolyfills.csAdds downlevel float.IsFinite.
src/libraries/Common/src/Polyfills/DoublePolyfills.csAdds downlevel double.IsFinite.
src/libraries/Common/src/Polyfills/BitArrayPolyfills.csAdds downlevel BitArray.HasAllSet().
src/libraries/Common/src/Polyfills/ArrayPolyfills.csAdds downlevel Array.MaxLength.
src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.csAdds downlevel DateTimeOffset.TotalOffsetMinutes.
src/libraries/Common/src/Polyfills/HashCodePolyfills.csAdds downlevel HashCode.AddBytes(ReadOnlySpan<byte>) as an extension method.
src/libraries/Common/src/Polyfills/EnvironmentPolyfills.csAdds downlevel Environment.ProcessId.
src/libraries/Common/src/Polyfills/GuidPolyfills.csAdds downlevel Guid.TryWriteBytes(Span<byte>).
src/libraries/Common/src/Polyfills/BitConverterPolyfills.csAdds downlevel BitConverter.SingleToUInt32Bits / DoubleToUInt64Bits.
src/libraries/Common/src/Polyfills/IReadOnlySetPolyfill.csAdds downlevel IReadOnlySet<T> (guarded by #if !NET).
src/libraries/Common/src/Polyfills/BitOperationsPolyfill.csAdds downlevel System.Numerics.BitOperations.RotateLeft (guarded by #if !NET).
src/libraries/Common/src/Polyfills/CollectionsPolyfills.csAdds downlevel KeyValuePair.Deconstruct extension method.
src/libraries/Common/src/Polyfills/DictionaryPolyfills.csAdds downlevel TryAdd / TryDequeue polyfills (new shared source).

Comment threadsrc/libraries/Common/src/Polyfills/EnvironmentPolyfills.cs Outdated
@github-actions

This comment has been minimized.

Leverage polyfills already in Common/src/Polyfills/ to remove #if NET
blocks across additional libraries:
- ArrayPolyfills: LengthBuckets.cs, ArrayInfo.cs (Nrbf)
- IReadOnlySetPolyfill: ImmutableSortedSet, ImmutableHashSet, FrozenSet
- StreamPolyfills: SoundPlayer.cs, XmlBuffer.cs (Syndication)
- DateTimeOffsetPolyfills: CacheEntry.cs, Rss20FeedFormatter, Atom10FeedFormatter
- HashCodePolyfills: CoseHeaderValue.cs
- EnvironmentPolyfills: Win32.cs (Hosting.WindowsServices), SystemdHelpers.cs cleanup
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

CopilotAI review requested due to automatic review settings March 30, 2026 02:49
- EnvironmentPolyfills: dispose Process instance to avoid handle leak
- DoublePolyfills/SinglePolyfills: use same bit-manipulation impl as
the real Double.IsFinite/Single.IsFinite in CoreLib
- IReadOnlySetPolyfill: add comment explaining why #if !NET is needed
- StreamPolyfills: add comment explaining the 81_920 buffer size constant
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New polyfill: RuntimeHelpersPolyfills.cs provides:
- TryEnsureSufficientExecutionStack (wraps EnsureSufficientExecutionStack)
- GetUninitializedObject (delegates to FormatterServices)
Cleaned up in System.Text.Json:
- StackHelper.cs: remove #if NET for TryEnsureSufficientExecutionStack
- FSharpUnionConverter.cs: remove #if NET for GetUninitializedObject
and dead conditional using
Skipped (not polyfillable with extension() blocks):
- JsonArray.cs: List.AddRange(ReadOnlySpan) and uint.TryFormat
- CharConverter.cs: new ReadOnlySpan<char>(in value) constructor
- JsonCamelCaseNamingPolicy.cs: string.Create (shared with generators)
- JsonReaderHelper.Unescaping.cs: Utf8.IsValid (different error handling)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 66 out of 66 changed files in this pull request and generated 2 comments.

Comment threadsrc/libraries/System.Text.Json/src/System/Text/Json/StackHelper.cs Outdated
@github-actions

This comment has been minimized.

- Fix stray carriage return in Caching.Memory csproj
- JsonConsoleFormatter: use stackalloc span for char writing instead of
ToString() to avoid allocation regression (copilot review)
- Delete StackHelper.cs, inline RuntimeHelpers.TryEnsureSufficientExecutionStack
call directly in JsonElement.cs (jkotas)
- Revert IReadOnlySet polyfill: restore #if NET guards on FrozenSet,
ImmutableHashSet, ImmutableSortedSet; keep IReadOnlySet<T> definition
in library-local Polyfills.cs (jkotas concern about subtle bugs)
- Rename BitOperationsPolyfill.cs -> BitOperationsPolyfills.cs for
consistent plural naming across all polyfill files
- Remove dead StreamPolyfills ref from System.Windows.Extensions
(only targets NETCoreApp, polyfill would never compile)
- Fix EncodingPolyfills Link path for directory structure consistency
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

- Fully revert all changes to SimpleConsoleFormatter.cs
- JsonConsoleFormatter: use [charValue] collection literal instead of
stackalloc + manual assignment
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 30, 2026 14:36
@EgorBo
EgorBo removed the request for review from CopilotMarch 31, 2026 13:27

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Observation — EncodingPolyfills.cs lives outside Polyfills/ directory
EncodingPolyfills.cs is at $(CommonPath)System\Text\EncodingPolyfills.cs (pre-existing file not moved by this PR) but is l...

Done in 0f64157. Moved EncodingPolyfills.cs from src/libraries/Common/src/System/Text/ to src/libraries/Common/src/Polyfills/, updated the file to use a file-scoped namespace, and updated the references in both Directory.Build.targets and Microsoft.Extensions.Logging.Console.csproj.

CopilotAI changed the title Move pollyfills for !NET targets to src/libraries/Common/src/Polyfills/Move polyfills for !NET targets to src/libraries/Common/src/Polyfills/Mar 31, 2026
CopilotAI requested a review from jkotasMarch 31, 2026 13:29
@EgorBo

EgorBo commented Mar 31, 2026

Copy link
Copy Markdown
MemberAuthor

Ah, I was not aware it finally is able to push into developers' PRs 🙂 . Although, I guess it's possible only because my local Copilot filed this PR in dotnet/runtime and not in EgorBo/runtime

@jkotas

Copy link
Copy Markdown
Member

Right, it works only for PRs in dotnet/runtime currently.

@jkotas

Copy link
Copy Markdown
Member

The polyfill creates and disposes a Process object on every access. This matches the pattern used by the old inline #else code and only runs on downlevel TFMs where Environment.ProcessId doesn't exist. On .NETCoreApp the real property is used instead (zero-cost)

@copilot Address this feedback by adding a simple cache

@EgorBo

Copy link
Copy Markdown
MemberAuthor

@copilot Address this feedback by adding a simple cache

I wasn't sure it in and left it as is. Actually, my impl is even better than whatever we had before - we didn't even dispose process objects.

CopilotAI review requested due to automatic review settings March 31, 2026 15:58
@EgorBo
EgorBo removed the request for review from CopilotMarch 31, 2026 15:58

CopilotAI commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

The polyfill creates and disposes a Process object on every access. This matches the pattern used by the old inline #else code and only runs on downlevel TFMs where Environment.ProcessId doesn't exi...

Done in f0bf71e. Added a s_processId static field initialized to 0 (not a valid PID) as the cache sentinel — the Process object is now only created once per process lifetime.

CopilotAI requested a review from jkotasMarch 31, 2026 15:59
@jkotas

Copy link
Copy Markdown
Member

I wasn't sure it in and left it as is

I think we want to avoid bad surprises when using polyfills.

@EgorBo
EgorBo enabled auto-merge (squash) April 1, 2026 00:27
@EgorBo

Copy link
Copy Markdown
MemberAuthor

/ba-g deadletter (WasmTestOnV8)

@jkotas

Copy link
Copy Markdown
Member

I have asked copilot to look for more https://github.com/dotnet/runtime/tasks/8e893e7e-9ea0-4c23-9402-5d3b9a8a0af2

jkotas added a commit that referenced this pull request Apr 1, 2026
Follow-up to #126287 — consolidates library-local `BitOperations`,
`Stream`, `StringBuilder`, and `Stack` polyfills under
`src/libraries/Common/src/Polyfills/`.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 1, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@EgorBo@jkotas