Consolidate downlevel polyfills under Common/src/Polyfills - #126391

Merged
jkotas merged 12 commits into
mainfrom
copilot/consolidate-downlevel-polyfills
Apr 1, 2026
Merged

Consolidate downlevel polyfills under Common/src/Polyfills#126391
jkotas merged 12 commits into
mainfrom
copilot/consolidate-downlevel-polyfills

Conversation

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #126287 — consolidates library-local BitOperations, Stream, StringBuilder, and Stack polyfills under src/libraries/Common/src/Polyfills/.

Changes

Common/src/Polyfills/BitOperationsPolyfills.cs

Expanded the existing polyfill (which only had RotateLeft(uint)) with the full set of methods needed by the affected libraries:

  • Log2(uint) (deBruijn-based)
  • PopCount(uint) and PopCount(ulong) (Hamming weight)
  • RotateLeft(ulong), RotateRight(uint), RotateRight(ulong)
  • TrailingZeroCount(uint) (deBruijn-based)

Common/src/Polyfills/StreamPolyfills.cs

Merged ReadExactly and CopyToAsync from the now-deleted Common/src/System/IO/StreamExtensions.netstandard.cs into the existing Stream polyfill (no System.Memory/System.Buffers dependencies).

Common/src/Polyfills/StreamMemoryPolyfills.cs(new)

New file containing the Memory-dependent stream extension methods, split out from StreamPolyfills.cs to avoid pulling in extra package references for consumers that don't need them:

  • ReadAsync(Memory<byte>, CancellationToken)
  • Write(ReadOnlyMemory<byte>)
  • WriteAsync(ReadOnlyMemory<byte>, CancellationToken)

Common/src/Polyfills/StringBuilderPolyfills.cs(new)

Moved from Common/src/System/Text/StringBuilderExtensions.cs and reworked to follow polyfill directory conventions (file-scoped namespace, extension(StringBuilder) block syntax):

  • Append(ReadOnlySpan<char>)

Common/src/Polyfills/StackPolyfills.cs(new)

Moved from System.Text.Json-local StackExtensions.netstandard.cs and reworked to follow polyfill directory conventions (file-scoped namespace, traditional extension methods matching DictionaryPolyfills.cs style for generic types):

  • TryPeek<T>(out T)
  • TryPop<T>(out T)

System.IO.Hashing

  • Removed local System/IO/Hashing/BitOperations.cs (had RotateLeft(uint/ulong))
  • Updated .csproj to reference BitOperationsPolyfills.cs and StreamPolyfills.cs

System.Text.Encodings.Web

  • Removed local Polyfills/System.Numerics.BitOperations.netstandard20.cs (had Log2)
  • Updated .csproj to reference BitOperationsPolyfills.cs

Microsoft.Bcl.Memory

  • Removed local Polyfills/System.Numerics.BitOperations.netstandard20.cs (had TrailingZeroCount, RotateLeft(uint), RotateRight(uint))
  • Updated .csproj to reference BitOperationsPolyfills.cs (which is a strict superset)

System.Reflection.Metadata

  • Deleted BitArithmetic.CountBits(uint/ulong) wrapper methods; all call sites (BitArithmetic.Align, MetadataSizes, PEHeaderBuilder) now call BitOperations.PopCount directly
  • Updated TagToTokenTests.cs to call BitOperations.PopCount directly (fixes .NETFramework build break)
  • Added BitOperationsPolyfills.cs include in .csproj for non-.NETCoreApp targets

System.Net.ServerSentEvents, System.Text.Json, Microsoft.Extensions.Logging.Console, System.Net.Http.WinHttpHandler.Functional.Tests

  • Reference StreamMemoryPolyfills.cs (use Memory-based stream methods)

System.IO.Pipelines

  • References both StreamPolyfills.cs (uses CopyToAsync) and StreamMemoryPolyfills.cs (uses ReadAsync(Memory<byte>))

System.Speech, System.ServiceModel.Syndication

  • Reference StreamPolyfills.cs only (use ReadExactly); no extra System.Memory/System.Buffers/System.Threading.Tasks.Extensions package references needed

System.Text.Json

  • Updated .csproj to reference StringBuilderPolyfills.cs and StackPolyfills.cs from Common/src/Polyfills/
  • Removed local StackExtensions.netstandard.cs

Note

This PR description was generated with the assistance of GitHub Copilot.

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

Copy link
Copy Markdown
Contributor

Note

This review was generated by Copilot.

🤖 Copilot Code Review — PR #126391

Holistic Assessment

Motivation: This PR continues the consolidation work from #126287 by merging three separate library-local BitOperations polyfill implementations (from System.IO.Hashing, System.Text.Encodings.Web, and System.Reflection.Metadata) into the shared Common/src/Polyfills/BitOperationsPolyfills.cs. The motivation is clear and well-justified — eliminating duplicate code and reducing #if NET/#else blocks.

Approach: The approach is consistent with the prior PR's pattern: centralize polyfills in a shared directory, update .csproj files to link the shared file for non-.NETCoreApp targets, and remove library-local copies. The consolidation is straightforward.

Summary: ❌ Needs Changes. The PR has a clear compilation error: a double opening brace in the Log2 method body that leaves the method unclosed. This must be fixed before merge. All other changes are clean.


Detailed Findings

❌ Compilation Error — Double { in Log2 method (BitOperationsPolyfills.cs:32-33)

Merge-blocking. The Log2 method has two consecutive opening braces but only one closing brace, creating an unbalanced brace structure that will not compile:

publicstaticintLog2(uintvalue){// ← line 32: method body opens{// ← line 33: extra block opens (from the old Log2SoftwareFallback body)// ... implementation ...returnUnsafe.AddByteOffset(...);}// ← line 50: closes only the inner block// method body is never closed!

Brace analysis of the entire file shows 8 opening braces vs 7 closing braces (final nesting depth 1, expected 0). All subsequent methods (PopCount, RotateLeft, etc.) end up syntactically nested inside the unclosed Log2 body, which is invalid C# — you cannot declare public static members inside a method.

Root cause: Commit 7ee20d2d ("Apply suggestion from @jkotas") merged the Log2 wrapper and Log2SoftwareFallback into a single method, but the replacement added a new opening brace for the merged method without removing the existing opening brace from the old Log2SoftwareFallback body.

Fix: Remove the extra { on line 33:

publicstaticintLog2(uintvalue){// No AggressiveInlining due to large method size// Has conventional contract 0->0 (Log(0) is undefined)

✅ Correctness — value | 1 removal in Log2 is safe

The original System.Text.Encodings.Web implementation called Log2SoftwareFallback(value | 1) to guarantee the 0 → 0 contract. The merged version drops the | 1. This is safe: when value = 0, the OR-shift cascade leaves it at 0, the DeBruijn multiplication yields index 0, and Log2DeBruijn[0] = 0, so Log2(0) correctly returns 0. All other inputs are unaffected since | 1 only set the lowest bit which doesn't change floor(log2(x)) for x > 0.

✅ Correctness — Algorithm implementations match BCL software fallbacks

The PopCount(uint), PopCount(ulong), TrailingZeroCount(uint), RotateLeft, and RotateRight implementations are faithful reproductions of the well-known software fallbacks (Hamming weight, DeBruijn sequences, bit rotations). These match the deleted library-local versions exactly.

✅ Consolidation — csproj changes are correct

All three .csproj files correctly:

  • Replace local file references with $(CommonPath)Polyfills\BitOperationsPolyfills.cs
  • Scope the reference to '$(TargetFrameworkIdentifier)' != '.NETCoreApp'
  • Use appropriate Link attributes for solution explorer organization

✅ Cleanup — BitArithmetic.cs#if NET removal is clean

Removing the #if NET/#else blocks in System.Reflection.Metadata's BitArithmetic.cs and unconditionally calling BitOperations.PopCount is correct — the polyfill provides the same implementation that was previously inlined under #else.

💡 Observation — Extra methods not currently consumed

The consolidated polyfill includes RotateRight(uint/ulong) and TrailingZeroCount(uint) which are not currently used by any of the three consuming libraries. This is fine for a shared polyfill (other libraries may need them in the future), but worth noting for awareness.

Generated by Code Review for issue #126391 ·

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

…rrupted BitOperationsPolyfills.cs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/7bf64e17-d8a0-4dfd-aef8-3510f3ab10bf
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI changed the title Consolidate BitOperations downlevel polyfills under Common/src/PolyfillsConsolidate downlevel polyfills under Common/src/PolyfillsApr 1, 2026
CopilotAI requested a review from jkotasApril 1, 2026 02:11
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs Outdated
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs
…fills.cs; remove extra package refs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/3ad89f56-0cfc-41a4-9ba9-74682f13da8b
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI requested a review from jkotasApril 1, 2026 02:50
@jkotas
jkotas marked this pull request as ready for review April 1, 2026 03:27
Comment threadsrc/libraries/System.Text.Json/src/System.Text.Json.csproj Outdated

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

This PR consolidates downlevel (TargetFrameworkIdentifier != .NETCoreApp) polyfills for System.Numerics.BitOperations and Memory-based System.IO.Stream APIs into src/libraries/Common/src/Polyfills/, and updates multiple library projects to consume the shared implementations instead of library-local copies.

Changes:

  • Expanded Common’s BitOperations polyfill and switched affected libraries to reference it from Common/src/Polyfills.
  • Split stream polyfills into StreamPolyfills (no System.Memory dependencies) and StreamMemoryPolyfills (Memory-based APIs) and updated consuming projects accordingly.
  • Removed library-local polyfill sources (e.g., in System.IO.Hashing and System.Text.Encodings.Web) and updated call sites (e.g., System.Reflection.Metadata) to use BitOperations.PopCount.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.

Show a summary per file
FileDescription
src/libraries/System.Text.Json/src/System.Text.Json.csprojSwitches downlevel stream polyfill include to StreamMemoryPolyfills.
src/libraries/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csprojReplaces local BitOperations polyfill with shared BitOperationsPolyfills.
src/libraries/System.Text.Encodings.Web/src/Polyfills/System.Numerics.BitOperations.netstandard20.csDeletes library-local Log2 polyfill implementation.
src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.csReplaces BitArithmetic.CountBits usage with BitOperations.PopCount.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataSizes.csSwitches external table count calculation to BitOperations.PopCount.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/BitArithmetic.csRemoves CountBits helpers and uses BitOperations.PopCount for alignment asserts.
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csprojAdds shared BitOperationsPolyfills.cs for non-.NETCoreApp targets.
src/libraries/System.Net.ServerSentEvents/src/System.Net.ServerSentEvents.csprojSwitches downlevel stream polyfill include to StreamMemoryPolyfills.
src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csprojAdds StreamPolyfills + StreamMemoryPolyfills for downlevel targets.
src/libraries/System.IO.Pipelines/src/System.IO.Pipelines.csprojReplaces prior stream polyfill include with StreamPolyfills + StreamMemoryPolyfills.
src/libraries/System.IO.Hashing/src/System/IO/Hashing/BitOperations.csDeletes library-local RotateLeft polyfill.
src/libraries/System.IO.Hashing/src/System.IO.Hashing.csprojReferences shared BitOperationsPolyfills + StreamPolyfills for downlevel targets.
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csprojReplaces prior stream polyfill include with StreamPolyfills + StreamMemoryPolyfills.
src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.csAdds Memory-based stream polyfills (ReadAsync, Write, WriteAsync) for downlevel TFMs.
src/libraries/Common/src/Polyfills/BitOperationsPolyfills.csExpands shared BitOperations polyfill surface (Log2/PopCount/rotates/TZC).
Comments suppressed due to low confidence (2)

src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.cs:19

  • In ReadAsync(Memory) the non-array-backed path rents a buffer from ArrayPool and then calls stream.ReadAsync(...) inline. If stream.ReadAsync throws synchronously (e.g., ObjectDisposedException / argument validation) before returning a Task, the rented buffer won't be returned and will leak. Consider capturing the Task inside a try/catch and returning the rented buffer in the catch before rethrowing, then passing the Task to FinishReadAsync for the awaited path.
    src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.cs:76
  • In WriteAsync(ReadOnlyMemory) the non-array-backed path rents a buffer and then calls stream.WriteAsync(...) without guarding against synchronous exceptions. If stream.WriteAsync throws before returning a Task, the rented buffer will leak because FinishWriteAsync won't run. Capture the Task in a try/catch (or use a try/finally around the call) and ensure ArrayPool.Return happens on the synchronous-throw path as well.

… BitOperations.PopCount
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/d03d466e-d184-4531-bacb-f840ed08f950
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
ContributorAuthor

src\libraries\System.Reflection.Metadata\tests\Metadata\TagToTokenTests.cs(164,89): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'BitArithmetic' does not contain a definition for 'CountBits'
...

Fixed in commit d5a297c. Updated TagToTokenTests.cs to call BitOperations.PopCount(...) directly instead of the deleted BitArithmetic.CountBits(...), and added using System.Numerics;.

@jkotas
jkotas requested a review from EgorBoApril 1, 2026 03:52
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
ContributorAuthor

src\libraries\System.Reflection.Metadata\tests\Metadata\TagToTokenTests.cs(164,89): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'BitArithmetic' does not contain a definition for 'CountBits'
...

Already fixed in commit d5a297cTagToTokenTests.cs now calls BitOperations.PopCount(...) directly with using System.Numerics;.

…/src/Polyfills/BitOperationsPolyfills.cs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/5fb39d78-46a7-43bd-977f-845acfa9afe4
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI requested a review from jkotasApril 1, 2026 04:01
@jkotas
jkotas merged commit c24c76a into mainApr 1, 2026
90 of 97 checks passed
@jkotas
jkotas deleted the copilot/consolidate-downlevel-polyfills branch April 1, 2026 20:12
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 2, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jkotas@EgorBo
, '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

Consolidate downlevel polyfills under Common/src/Polyfills - #126391

Merged
jkotas merged 12 commits into
mainfrom
copilot/consolidate-downlevel-polyfills
Apr 1, 2026
Merged

Consolidate downlevel polyfills under Common/src/Polyfills#126391
jkotas merged 12 commits into
mainfrom
copilot/consolidate-downlevel-polyfills

Conversation

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #126287 — consolidates library-local BitOperations, Stream, StringBuilder, and Stack polyfills under src/libraries/Common/src/Polyfills/.

Changes

Common/src/Polyfills/BitOperationsPolyfills.cs

Expanded the existing polyfill (which only had RotateLeft(uint)) with the full set of methods needed by the affected libraries:

  • Log2(uint) (deBruijn-based)
  • PopCount(uint) and PopCount(ulong) (Hamming weight)
  • RotateLeft(ulong), RotateRight(uint), RotateRight(ulong)
  • TrailingZeroCount(uint) (deBruijn-based)

Common/src/Polyfills/StreamPolyfills.cs

Merged ReadExactly and CopyToAsync from the now-deleted Common/src/System/IO/StreamExtensions.netstandard.cs into the existing Stream polyfill (no System.Memory/System.Buffers dependencies).

Common/src/Polyfills/StreamMemoryPolyfills.cs(new)

New file containing the Memory-dependent stream extension methods, split out from StreamPolyfills.cs to avoid pulling in extra package references for consumers that don't need them:

  • ReadAsync(Memory<byte>, CancellationToken)
  • Write(ReadOnlyMemory<byte>)
  • WriteAsync(ReadOnlyMemory<byte>, CancellationToken)

Common/src/Polyfills/StringBuilderPolyfills.cs(new)

Moved from Common/src/System/Text/StringBuilderExtensions.cs and reworked to follow polyfill directory conventions (file-scoped namespace, extension(StringBuilder) block syntax):

  • Append(ReadOnlySpan<char>)

Common/src/Polyfills/StackPolyfills.cs(new)

Moved from System.Text.Json-local StackExtensions.netstandard.cs and reworked to follow polyfill directory conventions (file-scoped namespace, traditional extension methods matching DictionaryPolyfills.cs style for generic types):

  • TryPeek<T>(out T)
  • TryPop<T>(out T)

System.IO.Hashing

  • Removed local System/IO/Hashing/BitOperations.cs (had RotateLeft(uint/ulong))
  • Updated .csproj to reference BitOperationsPolyfills.cs and StreamPolyfills.cs

System.Text.Encodings.Web

  • Removed local Polyfills/System.Numerics.BitOperations.netstandard20.cs (had Log2)
  • Updated .csproj to reference BitOperationsPolyfills.cs

Microsoft.Bcl.Memory

  • Removed local Polyfills/System.Numerics.BitOperations.netstandard20.cs (had TrailingZeroCount, RotateLeft(uint), RotateRight(uint))
  • Updated .csproj to reference BitOperationsPolyfills.cs (which is a strict superset)

System.Reflection.Metadata

  • Deleted BitArithmetic.CountBits(uint/ulong) wrapper methods; all call sites (BitArithmetic.Align, MetadataSizes, PEHeaderBuilder) now call BitOperations.PopCount directly
  • Updated TagToTokenTests.cs to call BitOperations.PopCount directly (fixes .NETFramework build break)
  • Added BitOperationsPolyfills.cs include in .csproj for non-.NETCoreApp targets

System.Net.ServerSentEvents, System.Text.Json, Microsoft.Extensions.Logging.Console, System.Net.Http.WinHttpHandler.Functional.Tests

  • Reference StreamMemoryPolyfills.cs (use Memory-based stream methods)

System.IO.Pipelines

  • References both StreamPolyfills.cs (uses CopyToAsync) and StreamMemoryPolyfills.cs (uses ReadAsync(Memory<byte>))

System.Speech, System.ServiceModel.Syndication

  • Reference StreamPolyfills.cs only (use ReadExactly); no extra System.Memory/System.Buffers/System.Threading.Tasks.Extensions package references needed

System.Text.Json

  • Updated .csproj to reference StringBuilderPolyfills.cs and StackPolyfills.cs from Common/src/Polyfills/
  • Removed local StackExtensions.netstandard.cs

Note

This PR description was generated with the assistance of GitHub Copilot.

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

Copy link
Copy Markdown
Contributor

Note

This review was generated by Copilot.

🤖 Copilot Code Review — PR #126391

Holistic Assessment

Motivation: This PR continues the consolidation work from #126287 by merging three separate library-local BitOperations polyfill implementations (from System.IO.Hashing, System.Text.Encodings.Web, and System.Reflection.Metadata) into the shared Common/src/Polyfills/BitOperationsPolyfills.cs. The motivation is clear and well-justified — eliminating duplicate code and reducing #if NET/#else blocks.

Approach: The approach is consistent with the prior PR's pattern: centralize polyfills in a shared directory, update .csproj files to link the shared file for non-.NETCoreApp targets, and remove library-local copies. The consolidation is straightforward.

Summary: ❌ Needs Changes. The PR has a clear compilation error: a double opening brace in the Log2 method body that leaves the method unclosed. This must be fixed before merge. All other changes are clean.


Detailed Findings

❌ Compilation Error — Double { in Log2 method (BitOperationsPolyfills.cs:32-33)

Merge-blocking. The Log2 method has two consecutive opening braces but only one closing brace, creating an unbalanced brace structure that will not compile:

publicstaticintLog2(uintvalue){// ← line 32: method body opens{// ← line 33: extra block opens (from the old Log2SoftwareFallback body)// ... implementation ...returnUnsafe.AddByteOffset(...);}// ← line 50: closes only the inner block// method body is never closed!

Brace analysis of the entire file shows 8 opening braces vs 7 closing braces (final nesting depth 1, expected 0). All subsequent methods (PopCount, RotateLeft, etc.) end up syntactically nested inside the unclosed Log2 body, which is invalid C# — you cannot declare public static members inside a method.

Root cause: Commit 7ee20d2d ("Apply suggestion from @jkotas") merged the Log2 wrapper and Log2SoftwareFallback into a single method, but the replacement added a new opening brace for the merged method without removing the existing opening brace from the old Log2SoftwareFallback body.

Fix: Remove the extra { on line 33:

publicstaticintLog2(uintvalue){// No AggressiveInlining due to large method size// Has conventional contract 0->0 (Log(0) is undefined)

✅ Correctness — value | 1 removal in Log2 is safe

The original System.Text.Encodings.Web implementation called Log2SoftwareFallback(value | 1) to guarantee the 0 → 0 contract. The merged version drops the | 1. This is safe: when value = 0, the OR-shift cascade leaves it at 0, the DeBruijn multiplication yields index 0, and Log2DeBruijn[0] = 0, so Log2(0) correctly returns 0. All other inputs are unaffected since | 1 only set the lowest bit which doesn't change floor(log2(x)) for x > 0.

✅ Correctness — Algorithm implementations match BCL software fallbacks

The PopCount(uint), PopCount(ulong), TrailingZeroCount(uint), RotateLeft, and RotateRight implementations are faithful reproductions of the well-known software fallbacks (Hamming weight, DeBruijn sequences, bit rotations). These match the deleted library-local versions exactly.

✅ Consolidation — csproj changes are correct

All three .csproj files correctly:

  • Replace local file references with $(CommonPath)Polyfills\BitOperationsPolyfills.cs
  • Scope the reference to '$(TargetFrameworkIdentifier)' != '.NETCoreApp'
  • Use appropriate Link attributes for solution explorer organization

✅ Cleanup — BitArithmetic.cs#if NET removal is clean

Removing the #if NET/#else blocks in System.Reflection.Metadata's BitArithmetic.cs and unconditionally calling BitOperations.PopCount is correct — the polyfill provides the same implementation that was previously inlined under #else.

💡 Observation — Extra methods not currently consumed

The consolidated polyfill includes RotateRight(uint/ulong) and TrailingZeroCount(uint) which are not currently used by any of the three consuming libraries. This is fine for a shared polyfill (other libraries may need them in the future), but worth noting for awareness.

Generated by Code Review for issue #126391 ·

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

…rrupted BitOperationsPolyfills.cs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/7bf64e17-d8a0-4dfd-aef8-3510f3ab10bf
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI changed the title Consolidate BitOperations downlevel polyfills under Common/src/PolyfillsConsolidate downlevel polyfills under Common/src/PolyfillsApr 1, 2026
CopilotAI requested a review from jkotasApril 1, 2026 02:11
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs Outdated
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs
…fills.cs; remove extra package refs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/3ad89f56-0cfc-41a4-9ba9-74682f13da8b
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI requested a review from jkotasApril 1, 2026 02:50
@jkotas
jkotas marked this pull request as ready for review April 1, 2026 03:27
Comment threadsrc/libraries/System.Text.Json/src/System.Text.Json.csproj Outdated

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

This PR consolidates downlevel (TargetFrameworkIdentifier != .NETCoreApp) polyfills for System.Numerics.BitOperations and Memory-based System.IO.Stream APIs into src/libraries/Common/src/Polyfills/, and updates multiple library projects to consume the shared implementations instead of library-local copies.

Changes:

  • Expanded Common’s BitOperations polyfill and switched affected libraries to reference it from Common/src/Polyfills.
  • Split stream polyfills into StreamPolyfills (no System.Memory dependencies) and StreamMemoryPolyfills (Memory-based APIs) and updated consuming projects accordingly.
  • Removed library-local polyfill sources (e.g., in System.IO.Hashing and System.Text.Encodings.Web) and updated call sites (e.g., System.Reflection.Metadata) to use BitOperations.PopCount.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.

Show a summary per file
FileDescription
src/libraries/System.Text.Json/src/System.Text.Json.csprojSwitches downlevel stream polyfill include to StreamMemoryPolyfills.
src/libraries/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csprojReplaces local BitOperations polyfill with shared BitOperationsPolyfills.
src/libraries/System.Text.Encodings.Web/src/Polyfills/System.Numerics.BitOperations.netstandard20.csDeletes library-local Log2 polyfill implementation.
src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.csReplaces BitArithmetic.CountBits usage with BitOperations.PopCount.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataSizes.csSwitches external table count calculation to BitOperations.PopCount.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/BitArithmetic.csRemoves CountBits helpers and uses BitOperations.PopCount for alignment asserts.
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csprojAdds shared BitOperationsPolyfills.cs for non-.NETCoreApp targets.
src/libraries/System.Net.ServerSentEvents/src/System.Net.ServerSentEvents.csprojSwitches downlevel stream polyfill include to StreamMemoryPolyfills.
src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csprojAdds StreamPolyfills + StreamMemoryPolyfills for downlevel targets.
src/libraries/System.IO.Pipelines/src/System.IO.Pipelines.csprojReplaces prior stream polyfill include with StreamPolyfills + StreamMemoryPolyfills.
src/libraries/System.IO.Hashing/src/System/IO/Hashing/BitOperations.csDeletes library-local RotateLeft polyfill.
src/libraries/System.IO.Hashing/src/System.IO.Hashing.csprojReferences shared BitOperationsPolyfills + StreamPolyfills for downlevel targets.
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csprojReplaces prior stream polyfill include with StreamPolyfills + StreamMemoryPolyfills.
src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.csAdds Memory-based stream polyfills (ReadAsync, Write, WriteAsync) for downlevel TFMs.
src/libraries/Common/src/Polyfills/BitOperationsPolyfills.csExpands shared BitOperations polyfill surface (Log2/PopCount/rotates/TZC).
Comments suppressed due to low confidence (2)

src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.cs:19

  • In ReadAsync(Memory) the non-array-backed path rents a buffer from ArrayPool and then calls stream.ReadAsync(...) inline. If stream.ReadAsync throws synchronously (e.g., ObjectDisposedException / argument validation) before returning a Task, the rented buffer won't be returned and will leak. Consider capturing the Task inside a try/catch and returning the rented buffer in the catch before rethrowing, then passing the Task to FinishReadAsync for the awaited path.
    src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.cs:76
  • In WriteAsync(ReadOnlyMemory) the non-array-backed path rents a buffer and then calls stream.WriteAsync(...) without guarding against synchronous exceptions. If stream.WriteAsync throws before returning a Task, the rented buffer will leak because FinishWriteAsync won't run. Capture the Task in a try/catch (or use a try/finally around the call) and ensure ArrayPool.Return happens on the synchronous-throw path as well.

… BitOperations.PopCount
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/d03d466e-d184-4531-bacb-f840ed08f950
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
ContributorAuthor

src\libraries\System.Reflection.Metadata\tests\Metadata\TagToTokenTests.cs(164,89): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'BitArithmetic' does not contain a definition for 'CountBits'
...

Fixed in commit d5a297c. Updated TagToTokenTests.cs to call BitOperations.PopCount(...) directly instead of the deleted BitArithmetic.CountBits(...), and added using System.Numerics;.

@jkotas
jkotas requested a review from EgorBoApril 1, 2026 03:52
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
ContributorAuthor

src\libraries\System.Reflection.Metadata\tests\Metadata\TagToTokenTests.cs(164,89): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'BitArithmetic' does not contain a definition for 'CountBits'
...

Already fixed in commit d5a297cTagToTokenTests.cs now calls BitOperations.PopCount(...) directly with using System.Numerics;.

…/src/Polyfills/BitOperationsPolyfills.cs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/5fb39d78-46a7-43bd-977f-845acfa9afe4
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI requested a review from jkotasApril 1, 2026 04:01
@jkotas
jkotas merged commit c24c76a into mainApr 1, 2026
90 of 97 checks passed
@jkotas
jkotas deleted the copilot/consolidate-downlevel-polyfills branch April 1, 2026 20:12
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 2, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jkotas@EgorBo
, '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

Consolidate downlevel polyfills under Common/src/Polyfills - #126391

Merged
jkotas merged 12 commits into
mainfrom
copilot/consolidate-downlevel-polyfills
Apr 1, 2026
Merged

Consolidate downlevel polyfills under Common/src/Polyfills#126391
jkotas merged 12 commits into
mainfrom
copilot/consolidate-downlevel-polyfills

Conversation

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #126287 — consolidates library-local BitOperations, Stream, StringBuilder, and Stack polyfills under src/libraries/Common/src/Polyfills/.

Changes

Common/src/Polyfills/BitOperationsPolyfills.cs

Expanded the existing polyfill (which only had RotateLeft(uint)) with the full set of methods needed by the affected libraries:

  • Log2(uint) (deBruijn-based)
  • PopCount(uint) and PopCount(ulong) (Hamming weight)
  • RotateLeft(ulong), RotateRight(uint), RotateRight(ulong)
  • TrailingZeroCount(uint) (deBruijn-based)

Common/src/Polyfills/StreamPolyfills.cs

Merged ReadExactly and CopyToAsync from the now-deleted Common/src/System/IO/StreamExtensions.netstandard.cs into the existing Stream polyfill (no System.Memory/System.Buffers dependencies).

Common/src/Polyfills/StreamMemoryPolyfills.cs(new)

New file containing the Memory-dependent stream extension methods, split out from StreamPolyfills.cs to avoid pulling in extra package references for consumers that don't need them:

  • ReadAsync(Memory<byte>, CancellationToken)
  • Write(ReadOnlyMemory<byte>)
  • WriteAsync(ReadOnlyMemory<byte>, CancellationToken)

Common/src/Polyfills/StringBuilderPolyfills.cs(new)

Moved from Common/src/System/Text/StringBuilderExtensions.cs and reworked to follow polyfill directory conventions (file-scoped namespace, extension(StringBuilder) block syntax):

  • Append(ReadOnlySpan<char>)

Common/src/Polyfills/StackPolyfills.cs(new)

Moved from System.Text.Json-local StackExtensions.netstandard.cs and reworked to follow polyfill directory conventions (file-scoped namespace, traditional extension methods matching DictionaryPolyfills.cs style for generic types):

  • TryPeek<T>(out T)
  • TryPop<T>(out T)

System.IO.Hashing

  • Removed local System/IO/Hashing/BitOperations.cs (had RotateLeft(uint/ulong))
  • Updated .csproj to reference BitOperationsPolyfills.cs and StreamPolyfills.cs

System.Text.Encodings.Web

  • Removed local Polyfills/System.Numerics.BitOperations.netstandard20.cs (had Log2)
  • Updated .csproj to reference BitOperationsPolyfills.cs

Microsoft.Bcl.Memory

  • Removed local Polyfills/System.Numerics.BitOperations.netstandard20.cs (had TrailingZeroCount, RotateLeft(uint), RotateRight(uint))
  • Updated .csproj to reference BitOperationsPolyfills.cs (which is a strict superset)

System.Reflection.Metadata

  • Deleted BitArithmetic.CountBits(uint/ulong) wrapper methods; all call sites (BitArithmetic.Align, MetadataSizes, PEHeaderBuilder) now call BitOperations.PopCount directly
  • Updated TagToTokenTests.cs to call BitOperations.PopCount directly (fixes .NETFramework build break)
  • Added BitOperationsPolyfills.cs include in .csproj for non-.NETCoreApp targets

System.Net.ServerSentEvents, System.Text.Json, Microsoft.Extensions.Logging.Console, System.Net.Http.WinHttpHandler.Functional.Tests

  • Reference StreamMemoryPolyfills.cs (use Memory-based stream methods)

System.IO.Pipelines

  • References both StreamPolyfills.cs (uses CopyToAsync) and StreamMemoryPolyfills.cs (uses ReadAsync(Memory<byte>))

System.Speech, System.ServiceModel.Syndication

  • Reference StreamPolyfills.cs only (use ReadExactly); no extra System.Memory/System.Buffers/System.Threading.Tasks.Extensions package references needed

System.Text.Json

  • Updated .csproj to reference StringBuilderPolyfills.cs and StackPolyfills.cs from Common/src/Polyfills/
  • Removed local StackExtensions.netstandard.cs

Note

This PR description was generated with the assistance of GitHub Copilot.

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

Copy link
Copy Markdown
Contributor

Note

This review was generated by Copilot.

🤖 Copilot Code Review — PR #126391

Holistic Assessment

Motivation: This PR continues the consolidation work from #126287 by merging three separate library-local BitOperations polyfill implementations (from System.IO.Hashing, System.Text.Encodings.Web, and System.Reflection.Metadata) into the shared Common/src/Polyfills/BitOperationsPolyfills.cs. The motivation is clear and well-justified — eliminating duplicate code and reducing #if NET/#else blocks.

Approach: The approach is consistent with the prior PR's pattern: centralize polyfills in a shared directory, update .csproj files to link the shared file for non-.NETCoreApp targets, and remove library-local copies. The consolidation is straightforward.

Summary: ❌ Needs Changes. The PR has a clear compilation error: a double opening brace in the Log2 method body that leaves the method unclosed. This must be fixed before merge. All other changes are clean.


Detailed Findings

❌ Compilation Error — Double { in Log2 method (BitOperationsPolyfills.cs:32-33)

Merge-blocking. The Log2 method has two consecutive opening braces but only one closing brace, creating an unbalanced brace structure that will not compile:

publicstaticintLog2(uintvalue){// ← line 32: method body opens{// ← line 33: extra block opens (from the old Log2SoftwareFallback body)// ... implementation ...returnUnsafe.AddByteOffset(...);}// ← line 50: closes only the inner block// method body is never closed!

Brace analysis of the entire file shows 8 opening braces vs 7 closing braces (final nesting depth 1, expected 0). All subsequent methods (PopCount, RotateLeft, etc.) end up syntactically nested inside the unclosed Log2 body, which is invalid C# — you cannot declare public static members inside a method.

Root cause: Commit 7ee20d2d ("Apply suggestion from @jkotas") merged the Log2 wrapper and Log2SoftwareFallback into a single method, but the replacement added a new opening brace for the merged method without removing the existing opening brace from the old Log2SoftwareFallback body.

Fix: Remove the extra { on line 33:

publicstaticintLog2(uintvalue){// No AggressiveInlining due to large method size// Has conventional contract 0->0 (Log(0) is undefined)

✅ Correctness — value | 1 removal in Log2 is safe

The original System.Text.Encodings.Web implementation called Log2SoftwareFallback(value | 1) to guarantee the 0 → 0 contract. The merged version drops the | 1. This is safe: when value = 0, the OR-shift cascade leaves it at 0, the DeBruijn multiplication yields index 0, and Log2DeBruijn[0] = 0, so Log2(0) correctly returns 0. All other inputs are unaffected since | 1 only set the lowest bit which doesn't change floor(log2(x)) for x > 0.

✅ Correctness — Algorithm implementations match BCL software fallbacks

The PopCount(uint), PopCount(ulong), TrailingZeroCount(uint), RotateLeft, and RotateRight implementations are faithful reproductions of the well-known software fallbacks (Hamming weight, DeBruijn sequences, bit rotations). These match the deleted library-local versions exactly.

✅ Consolidation — csproj changes are correct

All three .csproj files correctly:

  • Replace local file references with $(CommonPath)Polyfills\BitOperationsPolyfills.cs
  • Scope the reference to '$(TargetFrameworkIdentifier)' != '.NETCoreApp'
  • Use appropriate Link attributes for solution explorer organization

✅ Cleanup — BitArithmetic.cs#if NET removal is clean

Removing the #if NET/#else blocks in System.Reflection.Metadata's BitArithmetic.cs and unconditionally calling BitOperations.PopCount is correct — the polyfill provides the same implementation that was previously inlined under #else.

💡 Observation — Extra methods not currently consumed

The consolidated polyfill includes RotateRight(uint/ulong) and TrailingZeroCount(uint) which are not currently used by any of the three consuming libraries. This is fine for a shared polyfill (other libraries may need them in the future), but worth noting for awareness.

Generated by Code Review for issue #126391 ·

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

…rrupted BitOperationsPolyfills.cs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/7bf64e17-d8a0-4dfd-aef8-3510f3ab10bf
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI changed the title Consolidate BitOperations downlevel polyfills under Common/src/PolyfillsConsolidate downlevel polyfills under Common/src/PolyfillsApr 1, 2026
CopilotAI requested a review from jkotasApril 1, 2026 02:11
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs Outdated
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs
…fills.cs; remove extra package refs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/3ad89f56-0cfc-41a4-9ba9-74682f13da8b
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI requested a review from jkotasApril 1, 2026 02:50
@jkotas
jkotas marked this pull request as ready for review April 1, 2026 03:27
Comment threadsrc/libraries/System.Text.Json/src/System.Text.Json.csproj Outdated

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

This PR consolidates downlevel (TargetFrameworkIdentifier != .NETCoreApp) polyfills for System.Numerics.BitOperations and Memory-based System.IO.Stream APIs into src/libraries/Common/src/Polyfills/, and updates multiple library projects to consume the shared implementations instead of library-local copies.

Changes:

  • Expanded Common’s BitOperations polyfill and switched affected libraries to reference it from Common/src/Polyfills.
  • Split stream polyfills into StreamPolyfills (no System.Memory dependencies) and StreamMemoryPolyfills (Memory-based APIs) and updated consuming projects accordingly.
  • Removed library-local polyfill sources (e.g., in System.IO.Hashing and System.Text.Encodings.Web) and updated call sites (e.g., System.Reflection.Metadata) to use BitOperations.PopCount.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.

Show a summary per file
FileDescription
src/libraries/System.Text.Json/src/System.Text.Json.csprojSwitches downlevel stream polyfill include to StreamMemoryPolyfills.
src/libraries/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csprojReplaces local BitOperations polyfill with shared BitOperationsPolyfills.
src/libraries/System.Text.Encodings.Web/src/Polyfills/System.Numerics.BitOperations.netstandard20.csDeletes library-local Log2 polyfill implementation.
src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.csReplaces BitArithmetic.CountBits usage with BitOperations.PopCount.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataSizes.csSwitches external table count calculation to BitOperations.PopCount.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/BitArithmetic.csRemoves CountBits helpers and uses BitOperations.PopCount for alignment asserts.
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csprojAdds shared BitOperationsPolyfills.cs for non-.NETCoreApp targets.
src/libraries/System.Net.ServerSentEvents/src/System.Net.ServerSentEvents.csprojSwitches downlevel stream polyfill include to StreamMemoryPolyfills.
src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csprojAdds StreamPolyfills + StreamMemoryPolyfills for downlevel targets.
src/libraries/System.IO.Pipelines/src/System.IO.Pipelines.csprojReplaces prior stream polyfill include with StreamPolyfills + StreamMemoryPolyfills.
src/libraries/System.IO.Hashing/src/System/IO/Hashing/BitOperations.csDeletes library-local RotateLeft polyfill.
src/libraries/System.IO.Hashing/src/System.IO.Hashing.csprojReferences shared BitOperationsPolyfills + StreamPolyfills for downlevel targets.
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csprojReplaces prior stream polyfill include with StreamPolyfills + StreamMemoryPolyfills.
src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.csAdds Memory-based stream polyfills (ReadAsync, Write, WriteAsync) for downlevel TFMs.
src/libraries/Common/src/Polyfills/BitOperationsPolyfills.csExpands shared BitOperations polyfill surface (Log2/PopCount/rotates/TZC).
Comments suppressed due to low confidence (2)

src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.cs:19

  • In ReadAsync(Memory) the non-array-backed path rents a buffer from ArrayPool and then calls stream.ReadAsync(...) inline. If stream.ReadAsync throws synchronously (e.g., ObjectDisposedException / argument validation) before returning a Task, the rented buffer won't be returned and will leak. Consider capturing the Task inside a try/catch and returning the rented buffer in the catch before rethrowing, then passing the Task to FinishReadAsync for the awaited path.
    src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.cs:76
  • In WriteAsync(ReadOnlyMemory) the non-array-backed path rents a buffer and then calls stream.WriteAsync(...) without guarding against synchronous exceptions. If stream.WriteAsync throws before returning a Task, the rented buffer will leak because FinishWriteAsync won't run. Capture the Task in a try/catch (or use a try/finally around the call) and ensure ArrayPool.Return happens on the synchronous-throw path as well.

… BitOperations.PopCount
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/d03d466e-d184-4531-bacb-f840ed08f950
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
ContributorAuthor

src\libraries\System.Reflection.Metadata\tests\Metadata\TagToTokenTests.cs(164,89): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'BitArithmetic' does not contain a definition for 'CountBits'
...

Fixed in commit d5a297c. Updated TagToTokenTests.cs to call BitOperations.PopCount(...) directly instead of the deleted BitArithmetic.CountBits(...), and added using System.Numerics;.

@jkotas
jkotas requested a review from EgorBoApril 1, 2026 03:52
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
ContributorAuthor

src\libraries\System.Reflection.Metadata\tests\Metadata\TagToTokenTests.cs(164,89): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'BitArithmetic' does not contain a definition for 'CountBits'
...

Already fixed in commit d5a297cTagToTokenTests.cs now calls BitOperations.PopCount(...) directly with using System.Numerics;.

…/src/Polyfills/BitOperationsPolyfills.cs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/5fb39d78-46a7-43bd-977f-845acfa9afe4
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI requested a review from jkotasApril 1, 2026 04:01
@jkotas
jkotas merged commit c24c76a into mainApr 1, 2026
90 of 97 checks passed
@jkotas
jkotas deleted the copilot/consolidate-downlevel-polyfills branch April 1, 2026 20:12
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 2, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jkotas@EgorBo
, '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

Consolidate downlevel polyfills under Common/src/Polyfills - #126391

Merged
jkotas merged 12 commits into
mainfrom
copilot/consolidate-downlevel-polyfills
Apr 1, 2026
Merged

Consolidate downlevel polyfills under Common/src/Polyfills#126391
jkotas merged 12 commits into
mainfrom
copilot/consolidate-downlevel-polyfills

Conversation

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #126287 — consolidates library-local BitOperations, Stream, StringBuilder, and Stack polyfills under src/libraries/Common/src/Polyfills/.

Changes

Common/src/Polyfills/BitOperationsPolyfills.cs

Expanded the existing polyfill (which only had RotateLeft(uint)) with the full set of methods needed by the affected libraries:

  • Log2(uint) (deBruijn-based)
  • PopCount(uint) and PopCount(ulong) (Hamming weight)
  • RotateLeft(ulong), RotateRight(uint), RotateRight(ulong)
  • TrailingZeroCount(uint) (deBruijn-based)

Common/src/Polyfills/StreamPolyfills.cs

Merged ReadExactly and CopyToAsync from the now-deleted Common/src/System/IO/StreamExtensions.netstandard.cs into the existing Stream polyfill (no System.Memory/System.Buffers dependencies).

Common/src/Polyfills/StreamMemoryPolyfills.cs(new)

New file containing the Memory-dependent stream extension methods, split out from StreamPolyfills.cs to avoid pulling in extra package references for consumers that don't need them:

  • ReadAsync(Memory<byte>, CancellationToken)
  • Write(ReadOnlyMemory<byte>)
  • WriteAsync(ReadOnlyMemory<byte>, CancellationToken)

Common/src/Polyfills/StringBuilderPolyfills.cs(new)

Moved from Common/src/System/Text/StringBuilderExtensions.cs and reworked to follow polyfill directory conventions (file-scoped namespace, extension(StringBuilder) block syntax):

  • Append(ReadOnlySpan<char>)

Common/src/Polyfills/StackPolyfills.cs(new)

Moved from System.Text.Json-local StackExtensions.netstandard.cs and reworked to follow polyfill directory conventions (file-scoped namespace, traditional extension methods matching DictionaryPolyfills.cs style for generic types):

  • TryPeek<T>(out T)
  • TryPop<T>(out T)

System.IO.Hashing

  • Removed local System/IO/Hashing/BitOperations.cs (had RotateLeft(uint/ulong))
  • Updated .csproj to reference BitOperationsPolyfills.cs and StreamPolyfills.cs

System.Text.Encodings.Web

  • Removed local Polyfills/System.Numerics.BitOperations.netstandard20.cs (had Log2)
  • Updated .csproj to reference BitOperationsPolyfills.cs

Microsoft.Bcl.Memory

  • Removed local Polyfills/System.Numerics.BitOperations.netstandard20.cs (had TrailingZeroCount, RotateLeft(uint), RotateRight(uint))
  • Updated .csproj to reference BitOperationsPolyfills.cs (which is a strict superset)

System.Reflection.Metadata

  • Deleted BitArithmetic.CountBits(uint/ulong) wrapper methods; all call sites (BitArithmetic.Align, MetadataSizes, PEHeaderBuilder) now call BitOperations.PopCount directly
  • Updated TagToTokenTests.cs to call BitOperations.PopCount directly (fixes .NETFramework build break)
  • Added BitOperationsPolyfills.cs include in .csproj for non-.NETCoreApp targets

System.Net.ServerSentEvents, System.Text.Json, Microsoft.Extensions.Logging.Console, System.Net.Http.WinHttpHandler.Functional.Tests

  • Reference StreamMemoryPolyfills.cs (use Memory-based stream methods)

System.IO.Pipelines

  • References both StreamPolyfills.cs (uses CopyToAsync) and StreamMemoryPolyfills.cs (uses ReadAsync(Memory<byte>))

System.Speech, System.ServiceModel.Syndication

  • Reference StreamPolyfills.cs only (use ReadExactly); no extra System.Memory/System.Buffers/System.Threading.Tasks.Extensions package references needed

System.Text.Json

  • Updated .csproj to reference StringBuilderPolyfills.cs and StackPolyfills.cs from Common/src/Polyfills/
  • Removed local StackExtensions.netstandard.cs

Note

This PR description was generated with the assistance of GitHub Copilot.

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

Copy link
Copy Markdown
Contributor

Note

This review was generated by Copilot.

🤖 Copilot Code Review — PR #126391

Holistic Assessment

Motivation: This PR continues the consolidation work from #126287 by merging three separate library-local BitOperations polyfill implementations (from System.IO.Hashing, System.Text.Encodings.Web, and System.Reflection.Metadata) into the shared Common/src/Polyfills/BitOperationsPolyfills.cs. The motivation is clear and well-justified — eliminating duplicate code and reducing #if NET/#else blocks.

Approach: The approach is consistent with the prior PR's pattern: centralize polyfills in a shared directory, update .csproj files to link the shared file for non-.NETCoreApp targets, and remove library-local copies. The consolidation is straightforward.

Summary: ❌ Needs Changes. The PR has a clear compilation error: a double opening brace in the Log2 method body that leaves the method unclosed. This must be fixed before merge. All other changes are clean.


Detailed Findings

❌ Compilation Error — Double { in Log2 method (BitOperationsPolyfills.cs:32-33)

Merge-blocking. The Log2 method has two consecutive opening braces but only one closing brace, creating an unbalanced brace structure that will not compile:

publicstaticintLog2(uintvalue){// ← line 32: method body opens{// ← line 33: extra block opens (from the old Log2SoftwareFallback body)// ... implementation ...returnUnsafe.AddByteOffset(...);}// ← line 50: closes only the inner block// method body is never closed!

Brace analysis of the entire file shows 8 opening braces vs 7 closing braces (final nesting depth 1, expected 0). All subsequent methods (PopCount, RotateLeft, etc.) end up syntactically nested inside the unclosed Log2 body, which is invalid C# — you cannot declare public static members inside a method.

Root cause: Commit 7ee20d2d ("Apply suggestion from @jkotas") merged the Log2 wrapper and Log2SoftwareFallback into a single method, but the replacement added a new opening brace for the merged method without removing the existing opening brace from the old Log2SoftwareFallback body.

Fix: Remove the extra { on line 33:

publicstaticintLog2(uintvalue){// No AggressiveInlining due to large method size// Has conventional contract 0->0 (Log(0) is undefined)

✅ Correctness — value | 1 removal in Log2 is safe

The original System.Text.Encodings.Web implementation called Log2SoftwareFallback(value | 1) to guarantee the 0 → 0 contract. The merged version drops the | 1. This is safe: when value = 0, the OR-shift cascade leaves it at 0, the DeBruijn multiplication yields index 0, and Log2DeBruijn[0] = 0, so Log2(0) correctly returns 0. All other inputs are unaffected since | 1 only set the lowest bit which doesn't change floor(log2(x)) for x > 0.

✅ Correctness — Algorithm implementations match BCL software fallbacks

The PopCount(uint), PopCount(ulong), TrailingZeroCount(uint), RotateLeft, and RotateRight implementations are faithful reproductions of the well-known software fallbacks (Hamming weight, DeBruijn sequences, bit rotations). These match the deleted library-local versions exactly.

✅ Consolidation — csproj changes are correct

All three .csproj files correctly:

  • Replace local file references with $(CommonPath)Polyfills\BitOperationsPolyfills.cs
  • Scope the reference to '$(TargetFrameworkIdentifier)' != '.NETCoreApp'
  • Use appropriate Link attributes for solution explorer organization

✅ Cleanup — BitArithmetic.cs#if NET removal is clean

Removing the #if NET/#else blocks in System.Reflection.Metadata's BitArithmetic.cs and unconditionally calling BitOperations.PopCount is correct — the polyfill provides the same implementation that was previously inlined under #else.

💡 Observation — Extra methods not currently consumed

The consolidated polyfill includes RotateRight(uint/ulong) and TrailingZeroCount(uint) which are not currently used by any of the three consuming libraries. This is fine for a shared polyfill (other libraries may need them in the future), but worth noting for awareness.

Generated by Code Review for issue #126391 ·

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

…rrupted BitOperationsPolyfills.cs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/7bf64e17-d8a0-4dfd-aef8-3510f3ab10bf
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI changed the title Consolidate BitOperations downlevel polyfills under Common/src/PolyfillsConsolidate downlevel polyfills under Common/src/PolyfillsApr 1, 2026
CopilotAI requested a review from jkotasApril 1, 2026 02:11
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs Outdated
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs
…fills.cs; remove extra package refs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/3ad89f56-0cfc-41a4-9ba9-74682f13da8b
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI requested a review from jkotasApril 1, 2026 02:50
@jkotas
jkotas marked this pull request as ready for review April 1, 2026 03:27
Comment threadsrc/libraries/System.Text.Json/src/System.Text.Json.csproj Outdated

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

This PR consolidates downlevel (TargetFrameworkIdentifier != .NETCoreApp) polyfills for System.Numerics.BitOperations and Memory-based System.IO.Stream APIs into src/libraries/Common/src/Polyfills/, and updates multiple library projects to consume the shared implementations instead of library-local copies.

Changes:

  • Expanded Common’s BitOperations polyfill and switched affected libraries to reference it from Common/src/Polyfills.
  • Split stream polyfills into StreamPolyfills (no System.Memory dependencies) and StreamMemoryPolyfills (Memory-based APIs) and updated consuming projects accordingly.
  • Removed library-local polyfill sources (e.g., in System.IO.Hashing and System.Text.Encodings.Web) and updated call sites (e.g., System.Reflection.Metadata) to use BitOperations.PopCount.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.

Show a summary per file
FileDescription
src/libraries/System.Text.Json/src/System.Text.Json.csprojSwitches downlevel stream polyfill include to StreamMemoryPolyfills.
src/libraries/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csprojReplaces local BitOperations polyfill with shared BitOperationsPolyfills.
src/libraries/System.Text.Encodings.Web/src/Polyfills/System.Numerics.BitOperations.netstandard20.csDeletes library-local Log2 polyfill implementation.
src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.csReplaces BitArithmetic.CountBits usage with BitOperations.PopCount.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataSizes.csSwitches external table count calculation to BitOperations.PopCount.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/BitArithmetic.csRemoves CountBits helpers and uses BitOperations.PopCount for alignment asserts.
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csprojAdds shared BitOperationsPolyfills.cs for non-.NETCoreApp targets.
src/libraries/System.Net.ServerSentEvents/src/System.Net.ServerSentEvents.csprojSwitches downlevel stream polyfill include to StreamMemoryPolyfills.
src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csprojAdds StreamPolyfills + StreamMemoryPolyfills for downlevel targets.
src/libraries/System.IO.Pipelines/src/System.IO.Pipelines.csprojReplaces prior stream polyfill include with StreamPolyfills + StreamMemoryPolyfills.
src/libraries/System.IO.Hashing/src/System/IO/Hashing/BitOperations.csDeletes library-local RotateLeft polyfill.
src/libraries/System.IO.Hashing/src/System.IO.Hashing.csprojReferences shared BitOperationsPolyfills + StreamPolyfills for downlevel targets.
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csprojReplaces prior stream polyfill include with StreamPolyfills + StreamMemoryPolyfills.
src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.csAdds Memory-based stream polyfills (ReadAsync, Write, WriteAsync) for downlevel TFMs.
src/libraries/Common/src/Polyfills/BitOperationsPolyfills.csExpands shared BitOperations polyfill surface (Log2/PopCount/rotates/TZC).
Comments suppressed due to low confidence (2)

src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.cs:19

  • In ReadAsync(Memory) the non-array-backed path rents a buffer from ArrayPool and then calls stream.ReadAsync(...) inline. If stream.ReadAsync throws synchronously (e.g., ObjectDisposedException / argument validation) before returning a Task, the rented buffer won't be returned and will leak. Consider capturing the Task inside a try/catch and returning the rented buffer in the catch before rethrowing, then passing the Task to FinishReadAsync for the awaited path.
    src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.cs:76
  • In WriteAsync(ReadOnlyMemory) the non-array-backed path rents a buffer and then calls stream.WriteAsync(...) without guarding against synchronous exceptions. If stream.WriteAsync throws before returning a Task, the rented buffer will leak because FinishWriteAsync won't run. Capture the Task in a try/catch (or use a try/finally around the call) and ensure ArrayPool.Return happens on the synchronous-throw path as well.

… BitOperations.PopCount
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/d03d466e-d184-4531-bacb-f840ed08f950
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
ContributorAuthor

src\libraries\System.Reflection.Metadata\tests\Metadata\TagToTokenTests.cs(164,89): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'BitArithmetic' does not contain a definition for 'CountBits'
...

Fixed in commit d5a297c. Updated TagToTokenTests.cs to call BitOperations.PopCount(...) directly instead of the deleted BitArithmetic.CountBits(...), and added using System.Numerics;.

@jkotas
jkotas requested a review from EgorBoApril 1, 2026 03:52
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
ContributorAuthor

src\libraries\System.Reflection.Metadata\tests\Metadata\TagToTokenTests.cs(164,89): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'BitArithmetic' does not contain a definition for 'CountBits'
...

Already fixed in commit d5a297cTagToTokenTests.cs now calls BitOperations.PopCount(...) directly with using System.Numerics;.

…/src/Polyfills/BitOperationsPolyfills.cs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/5fb39d78-46a7-43bd-977f-845acfa9afe4
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI requested a review from jkotasApril 1, 2026 04:01
@jkotas
jkotas merged commit c24c76a into mainApr 1, 2026
90 of 97 checks passed
@jkotas
jkotas deleted the copilot/consolidate-downlevel-polyfills branch April 1, 2026 20:12
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 2, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jkotas@EgorBo
, '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

Consolidate downlevel polyfills under Common/src/Polyfills - #126391

Merged
jkotas merged 12 commits into
mainfrom
copilot/consolidate-downlevel-polyfills
Apr 1, 2026
Merged

Consolidate downlevel polyfills under Common/src/Polyfills#126391
jkotas merged 12 commits into
mainfrom
copilot/consolidate-downlevel-polyfills

Conversation

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #126287 — consolidates library-local BitOperations, Stream, StringBuilder, and Stack polyfills under src/libraries/Common/src/Polyfills/.

Changes

Common/src/Polyfills/BitOperationsPolyfills.cs

Expanded the existing polyfill (which only had RotateLeft(uint)) with the full set of methods needed by the affected libraries:

  • Log2(uint) (deBruijn-based)
  • PopCount(uint) and PopCount(ulong) (Hamming weight)
  • RotateLeft(ulong), RotateRight(uint), RotateRight(ulong)
  • TrailingZeroCount(uint) (deBruijn-based)

Common/src/Polyfills/StreamPolyfills.cs

Merged ReadExactly and CopyToAsync from the now-deleted Common/src/System/IO/StreamExtensions.netstandard.cs into the existing Stream polyfill (no System.Memory/System.Buffers dependencies).

Common/src/Polyfills/StreamMemoryPolyfills.cs(new)

New file containing the Memory-dependent stream extension methods, split out from StreamPolyfills.cs to avoid pulling in extra package references for consumers that don't need them:

  • ReadAsync(Memory<byte>, CancellationToken)
  • Write(ReadOnlyMemory<byte>)
  • WriteAsync(ReadOnlyMemory<byte>, CancellationToken)

Common/src/Polyfills/StringBuilderPolyfills.cs(new)

Moved from Common/src/System/Text/StringBuilderExtensions.cs and reworked to follow polyfill directory conventions (file-scoped namespace, extension(StringBuilder) block syntax):

  • Append(ReadOnlySpan<char>)

Common/src/Polyfills/StackPolyfills.cs(new)

Moved from System.Text.Json-local StackExtensions.netstandard.cs and reworked to follow polyfill directory conventions (file-scoped namespace, traditional extension methods matching DictionaryPolyfills.cs style for generic types):

  • TryPeek<T>(out T)
  • TryPop<T>(out T)

System.IO.Hashing

  • Removed local System/IO/Hashing/BitOperations.cs (had RotateLeft(uint/ulong))
  • Updated .csproj to reference BitOperationsPolyfills.cs and StreamPolyfills.cs

System.Text.Encodings.Web

  • Removed local Polyfills/System.Numerics.BitOperations.netstandard20.cs (had Log2)
  • Updated .csproj to reference BitOperationsPolyfills.cs

Microsoft.Bcl.Memory

  • Removed local Polyfills/System.Numerics.BitOperations.netstandard20.cs (had TrailingZeroCount, RotateLeft(uint), RotateRight(uint))
  • Updated .csproj to reference BitOperationsPolyfills.cs (which is a strict superset)

System.Reflection.Metadata

  • Deleted BitArithmetic.CountBits(uint/ulong) wrapper methods; all call sites (BitArithmetic.Align, MetadataSizes, PEHeaderBuilder) now call BitOperations.PopCount directly
  • Updated TagToTokenTests.cs to call BitOperations.PopCount directly (fixes .NETFramework build break)
  • Added BitOperationsPolyfills.cs include in .csproj for non-.NETCoreApp targets

System.Net.ServerSentEvents, System.Text.Json, Microsoft.Extensions.Logging.Console, System.Net.Http.WinHttpHandler.Functional.Tests

  • Reference StreamMemoryPolyfills.cs (use Memory-based stream methods)

System.IO.Pipelines

  • References both StreamPolyfills.cs (uses CopyToAsync) and StreamMemoryPolyfills.cs (uses ReadAsync(Memory<byte>))

System.Speech, System.ServiceModel.Syndication

  • Reference StreamPolyfills.cs only (use ReadExactly); no extra System.Memory/System.Buffers/System.Threading.Tasks.Extensions package references needed

System.Text.Json

  • Updated .csproj to reference StringBuilderPolyfills.cs and StackPolyfills.cs from Common/src/Polyfills/
  • Removed local StackExtensions.netstandard.cs

Note

This PR description was generated with the assistance of GitHub Copilot.

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

Copy link
Copy Markdown
Contributor

Note

This review was generated by Copilot.

🤖 Copilot Code Review — PR #126391

Holistic Assessment

Motivation: This PR continues the consolidation work from #126287 by merging three separate library-local BitOperations polyfill implementations (from System.IO.Hashing, System.Text.Encodings.Web, and System.Reflection.Metadata) into the shared Common/src/Polyfills/BitOperationsPolyfills.cs. The motivation is clear and well-justified — eliminating duplicate code and reducing #if NET/#else blocks.

Approach: The approach is consistent with the prior PR's pattern: centralize polyfills in a shared directory, update .csproj files to link the shared file for non-.NETCoreApp targets, and remove library-local copies. The consolidation is straightforward.

Summary: ❌ Needs Changes. The PR has a clear compilation error: a double opening brace in the Log2 method body that leaves the method unclosed. This must be fixed before merge. All other changes are clean.


Detailed Findings

❌ Compilation Error — Double { in Log2 method (BitOperationsPolyfills.cs:32-33)

Merge-blocking. The Log2 method has two consecutive opening braces but only one closing brace, creating an unbalanced brace structure that will not compile:

publicstaticintLog2(uintvalue){// ← line 32: method body opens{// ← line 33: extra block opens (from the old Log2SoftwareFallback body)// ... implementation ...returnUnsafe.AddByteOffset(...);}// ← line 50: closes only the inner block// method body is never closed!

Brace analysis of the entire file shows 8 opening braces vs 7 closing braces (final nesting depth 1, expected 0). All subsequent methods (PopCount, RotateLeft, etc.) end up syntactically nested inside the unclosed Log2 body, which is invalid C# — you cannot declare public static members inside a method.

Root cause: Commit 7ee20d2d ("Apply suggestion from @jkotas") merged the Log2 wrapper and Log2SoftwareFallback into a single method, but the replacement added a new opening brace for the merged method without removing the existing opening brace from the old Log2SoftwareFallback body.

Fix: Remove the extra { on line 33:

publicstaticintLog2(uintvalue){// No AggressiveInlining due to large method size// Has conventional contract 0->0 (Log(0) is undefined)

✅ Correctness — value | 1 removal in Log2 is safe

The original System.Text.Encodings.Web implementation called Log2SoftwareFallback(value | 1) to guarantee the 0 → 0 contract. The merged version drops the | 1. This is safe: when value = 0, the OR-shift cascade leaves it at 0, the DeBruijn multiplication yields index 0, and Log2DeBruijn[0] = 0, so Log2(0) correctly returns 0. All other inputs are unaffected since | 1 only set the lowest bit which doesn't change floor(log2(x)) for x > 0.

✅ Correctness — Algorithm implementations match BCL software fallbacks

The PopCount(uint), PopCount(ulong), TrailingZeroCount(uint), RotateLeft, and RotateRight implementations are faithful reproductions of the well-known software fallbacks (Hamming weight, DeBruijn sequences, bit rotations). These match the deleted library-local versions exactly.

✅ Consolidation — csproj changes are correct

All three .csproj files correctly:

  • Replace local file references with $(CommonPath)Polyfills\BitOperationsPolyfills.cs
  • Scope the reference to '$(TargetFrameworkIdentifier)' != '.NETCoreApp'
  • Use appropriate Link attributes for solution explorer organization

✅ Cleanup — BitArithmetic.cs#if NET removal is clean

Removing the #if NET/#else blocks in System.Reflection.Metadata's BitArithmetic.cs and unconditionally calling BitOperations.PopCount is correct — the polyfill provides the same implementation that was previously inlined under #else.

💡 Observation — Extra methods not currently consumed

The consolidated polyfill includes RotateRight(uint/ulong) and TrailingZeroCount(uint) which are not currently used by any of the three consuming libraries. This is fine for a shared polyfill (other libraries may need them in the future), but worth noting for awareness.

Generated by Code Review for issue #126391 ·

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

…rrupted BitOperationsPolyfills.cs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/7bf64e17-d8a0-4dfd-aef8-3510f3ab10bf
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI changed the title Consolidate BitOperations downlevel polyfills under Common/src/PolyfillsConsolidate downlevel polyfills under Common/src/PolyfillsApr 1, 2026
CopilotAI requested a review from jkotasApril 1, 2026 02:11
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs Outdated
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs
…fills.cs; remove extra package refs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/3ad89f56-0cfc-41a4-9ba9-74682f13da8b
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI requested a review from jkotasApril 1, 2026 02:50
@jkotas
jkotas marked this pull request as ready for review April 1, 2026 03:27
Comment threadsrc/libraries/System.Text.Json/src/System.Text.Json.csproj Outdated

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

This PR consolidates downlevel (TargetFrameworkIdentifier != .NETCoreApp) polyfills for System.Numerics.BitOperations and Memory-based System.IO.Stream APIs into src/libraries/Common/src/Polyfills/, and updates multiple library projects to consume the shared implementations instead of library-local copies.

Changes:

  • Expanded Common’s BitOperations polyfill and switched affected libraries to reference it from Common/src/Polyfills.
  • Split stream polyfills into StreamPolyfills (no System.Memory dependencies) and StreamMemoryPolyfills (Memory-based APIs) and updated consuming projects accordingly.
  • Removed library-local polyfill sources (e.g., in System.IO.Hashing and System.Text.Encodings.Web) and updated call sites (e.g., System.Reflection.Metadata) to use BitOperations.PopCount.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.

Show a summary per file
FileDescription
src/libraries/System.Text.Json/src/System.Text.Json.csprojSwitches downlevel stream polyfill include to StreamMemoryPolyfills.
src/libraries/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csprojReplaces local BitOperations polyfill with shared BitOperationsPolyfills.
src/libraries/System.Text.Encodings.Web/src/Polyfills/System.Numerics.BitOperations.netstandard20.csDeletes library-local Log2 polyfill implementation.
src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.csReplaces BitArithmetic.CountBits usage with BitOperations.PopCount.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataSizes.csSwitches external table count calculation to BitOperations.PopCount.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/BitArithmetic.csRemoves CountBits helpers and uses BitOperations.PopCount for alignment asserts.
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csprojAdds shared BitOperationsPolyfills.cs for non-.NETCoreApp targets.
src/libraries/System.Net.ServerSentEvents/src/System.Net.ServerSentEvents.csprojSwitches downlevel stream polyfill include to StreamMemoryPolyfills.
src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csprojAdds StreamPolyfills + StreamMemoryPolyfills for downlevel targets.
src/libraries/System.IO.Pipelines/src/System.IO.Pipelines.csprojReplaces prior stream polyfill include with StreamPolyfills + StreamMemoryPolyfills.
src/libraries/System.IO.Hashing/src/System/IO/Hashing/BitOperations.csDeletes library-local RotateLeft polyfill.
src/libraries/System.IO.Hashing/src/System.IO.Hashing.csprojReferences shared BitOperationsPolyfills + StreamPolyfills for downlevel targets.
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csprojReplaces prior stream polyfill include with StreamPolyfills + StreamMemoryPolyfills.
src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.csAdds Memory-based stream polyfills (ReadAsync, Write, WriteAsync) for downlevel TFMs.
src/libraries/Common/src/Polyfills/BitOperationsPolyfills.csExpands shared BitOperations polyfill surface (Log2/PopCount/rotates/TZC).
Comments suppressed due to low confidence (2)

src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.cs:19

  • In ReadAsync(Memory) the non-array-backed path rents a buffer from ArrayPool and then calls stream.ReadAsync(...) inline. If stream.ReadAsync throws synchronously (e.g., ObjectDisposedException / argument validation) before returning a Task, the rented buffer won't be returned and will leak. Consider capturing the Task inside a try/catch and returning the rented buffer in the catch before rethrowing, then passing the Task to FinishReadAsync for the awaited path.
    src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.cs:76
  • In WriteAsync(ReadOnlyMemory) the non-array-backed path rents a buffer and then calls stream.WriteAsync(...) without guarding against synchronous exceptions. If stream.WriteAsync throws before returning a Task, the rented buffer will leak because FinishWriteAsync won't run. Capture the Task in a try/catch (or use a try/finally around the call) and ensure ArrayPool.Return happens on the synchronous-throw path as well.

… BitOperations.PopCount
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/d03d466e-d184-4531-bacb-f840ed08f950
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
ContributorAuthor

src\libraries\System.Reflection.Metadata\tests\Metadata\TagToTokenTests.cs(164,89): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'BitArithmetic' does not contain a definition for 'CountBits'
...

Fixed in commit d5a297c. Updated TagToTokenTests.cs to call BitOperations.PopCount(...) directly instead of the deleted BitArithmetic.CountBits(...), and added using System.Numerics;.

@jkotas
jkotas requested a review from EgorBoApril 1, 2026 03:52
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
ContributorAuthor

src\libraries\System.Reflection.Metadata\tests\Metadata\TagToTokenTests.cs(164,89): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'BitArithmetic' does not contain a definition for 'CountBits'
...

Already fixed in commit d5a297cTagToTokenTests.cs now calls BitOperations.PopCount(...) directly with using System.Numerics;.

…/src/Polyfills/BitOperationsPolyfills.cs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/5fb39d78-46a7-43bd-977f-845acfa9afe4
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI requested a review from jkotasApril 1, 2026 04:01
@jkotas
jkotas merged commit c24c76a into mainApr 1, 2026
90 of 97 checks passed
@jkotas
jkotas deleted the copilot/consolidate-downlevel-polyfills branch April 1, 2026 20:12
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 2, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jkotas@EgorBo
, '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

Consolidate downlevel polyfills under Common/src/Polyfills - #126391

Merged
jkotas merged 12 commits into
mainfrom
copilot/consolidate-downlevel-polyfills
Apr 1, 2026
Merged

Consolidate downlevel polyfills under Common/src/Polyfills#126391
jkotas merged 12 commits into
mainfrom
copilot/consolidate-downlevel-polyfills

Conversation

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #126287 — consolidates library-local BitOperations, Stream, StringBuilder, and Stack polyfills under src/libraries/Common/src/Polyfills/.

Changes

Common/src/Polyfills/BitOperationsPolyfills.cs

Expanded the existing polyfill (which only had RotateLeft(uint)) with the full set of methods needed by the affected libraries:

  • Log2(uint) (deBruijn-based)
  • PopCount(uint) and PopCount(ulong) (Hamming weight)
  • RotateLeft(ulong), RotateRight(uint), RotateRight(ulong)
  • TrailingZeroCount(uint) (deBruijn-based)

Common/src/Polyfills/StreamPolyfills.cs

Merged ReadExactly and CopyToAsync from the now-deleted Common/src/System/IO/StreamExtensions.netstandard.cs into the existing Stream polyfill (no System.Memory/System.Buffers dependencies).

Common/src/Polyfills/StreamMemoryPolyfills.cs(new)

New file containing the Memory-dependent stream extension methods, split out from StreamPolyfills.cs to avoid pulling in extra package references for consumers that don't need them:

  • ReadAsync(Memory<byte>, CancellationToken)
  • Write(ReadOnlyMemory<byte>)
  • WriteAsync(ReadOnlyMemory<byte>, CancellationToken)

Common/src/Polyfills/StringBuilderPolyfills.cs(new)

Moved from Common/src/System/Text/StringBuilderExtensions.cs and reworked to follow polyfill directory conventions (file-scoped namespace, extension(StringBuilder) block syntax):

  • Append(ReadOnlySpan<char>)

Common/src/Polyfills/StackPolyfills.cs(new)

Moved from System.Text.Json-local StackExtensions.netstandard.cs and reworked to follow polyfill directory conventions (file-scoped namespace, traditional extension methods matching DictionaryPolyfills.cs style for generic types):

  • TryPeek<T>(out T)
  • TryPop<T>(out T)

System.IO.Hashing

  • Removed local System/IO/Hashing/BitOperations.cs (had RotateLeft(uint/ulong))
  • Updated .csproj to reference BitOperationsPolyfills.cs and StreamPolyfills.cs

System.Text.Encodings.Web

  • Removed local Polyfills/System.Numerics.BitOperations.netstandard20.cs (had Log2)
  • Updated .csproj to reference BitOperationsPolyfills.cs

Microsoft.Bcl.Memory

  • Removed local Polyfills/System.Numerics.BitOperations.netstandard20.cs (had TrailingZeroCount, RotateLeft(uint), RotateRight(uint))
  • Updated .csproj to reference BitOperationsPolyfills.cs (which is a strict superset)

System.Reflection.Metadata

  • Deleted BitArithmetic.CountBits(uint/ulong) wrapper methods; all call sites (BitArithmetic.Align, MetadataSizes, PEHeaderBuilder) now call BitOperations.PopCount directly
  • Updated TagToTokenTests.cs to call BitOperations.PopCount directly (fixes .NETFramework build break)
  • Added BitOperationsPolyfills.cs include in .csproj for non-.NETCoreApp targets

System.Net.ServerSentEvents, System.Text.Json, Microsoft.Extensions.Logging.Console, System.Net.Http.WinHttpHandler.Functional.Tests

  • Reference StreamMemoryPolyfills.cs (use Memory-based stream methods)

System.IO.Pipelines

  • References both StreamPolyfills.cs (uses CopyToAsync) and StreamMemoryPolyfills.cs (uses ReadAsync(Memory<byte>))

System.Speech, System.ServiceModel.Syndication

  • Reference StreamPolyfills.cs only (use ReadExactly); no extra System.Memory/System.Buffers/System.Threading.Tasks.Extensions package references needed

System.Text.Json

  • Updated .csproj to reference StringBuilderPolyfills.cs and StackPolyfills.cs from Common/src/Polyfills/
  • Removed local StackExtensions.netstandard.cs

Note

This PR description was generated with the assistance of GitHub Copilot.

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

Copy link
Copy Markdown
Contributor

Note

This review was generated by Copilot.

🤖 Copilot Code Review — PR #126391

Holistic Assessment

Motivation: This PR continues the consolidation work from #126287 by merging three separate library-local BitOperations polyfill implementations (from System.IO.Hashing, System.Text.Encodings.Web, and System.Reflection.Metadata) into the shared Common/src/Polyfills/BitOperationsPolyfills.cs. The motivation is clear and well-justified — eliminating duplicate code and reducing #if NET/#else blocks.

Approach: The approach is consistent with the prior PR's pattern: centralize polyfills in a shared directory, update .csproj files to link the shared file for non-.NETCoreApp targets, and remove library-local copies. The consolidation is straightforward.

Summary: ❌ Needs Changes. The PR has a clear compilation error: a double opening brace in the Log2 method body that leaves the method unclosed. This must be fixed before merge. All other changes are clean.


Detailed Findings

❌ Compilation Error — Double { in Log2 method (BitOperationsPolyfills.cs:32-33)

Merge-blocking. The Log2 method has two consecutive opening braces but only one closing brace, creating an unbalanced brace structure that will not compile:

publicstaticintLog2(uintvalue){// ← line 32: method body opens{// ← line 33: extra block opens (from the old Log2SoftwareFallback body)// ... implementation ...returnUnsafe.AddByteOffset(...);}// ← line 50: closes only the inner block// method body is never closed!

Brace analysis of the entire file shows 8 opening braces vs 7 closing braces (final nesting depth 1, expected 0). All subsequent methods (PopCount, RotateLeft, etc.) end up syntactically nested inside the unclosed Log2 body, which is invalid C# — you cannot declare public static members inside a method.

Root cause: Commit 7ee20d2d ("Apply suggestion from @jkotas") merged the Log2 wrapper and Log2SoftwareFallback into a single method, but the replacement added a new opening brace for the merged method without removing the existing opening brace from the old Log2SoftwareFallback body.

Fix: Remove the extra { on line 33:

publicstaticintLog2(uintvalue){// No AggressiveInlining due to large method size// Has conventional contract 0->0 (Log(0) is undefined)

✅ Correctness — value | 1 removal in Log2 is safe

The original System.Text.Encodings.Web implementation called Log2SoftwareFallback(value | 1) to guarantee the 0 → 0 contract. The merged version drops the | 1. This is safe: when value = 0, the OR-shift cascade leaves it at 0, the DeBruijn multiplication yields index 0, and Log2DeBruijn[0] = 0, so Log2(0) correctly returns 0. All other inputs are unaffected since | 1 only set the lowest bit which doesn't change floor(log2(x)) for x > 0.

✅ Correctness — Algorithm implementations match BCL software fallbacks

The PopCount(uint), PopCount(ulong), TrailingZeroCount(uint), RotateLeft, and RotateRight implementations are faithful reproductions of the well-known software fallbacks (Hamming weight, DeBruijn sequences, bit rotations). These match the deleted library-local versions exactly.

✅ Consolidation — csproj changes are correct

All three .csproj files correctly:

  • Replace local file references with $(CommonPath)Polyfills\BitOperationsPolyfills.cs
  • Scope the reference to '$(TargetFrameworkIdentifier)' != '.NETCoreApp'
  • Use appropriate Link attributes for solution explorer organization

✅ Cleanup — BitArithmetic.cs#if NET removal is clean

Removing the #if NET/#else blocks in System.Reflection.Metadata's BitArithmetic.cs and unconditionally calling BitOperations.PopCount is correct — the polyfill provides the same implementation that was previously inlined under #else.

💡 Observation — Extra methods not currently consumed

The consolidated polyfill includes RotateRight(uint/ulong) and TrailingZeroCount(uint) which are not currently used by any of the three consuming libraries. This is fine for a shared polyfill (other libraries may need them in the future), but worth noting for awareness.

Generated by Code Review for issue #126391 ·

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

…rrupted BitOperationsPolyfills.cs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/7bf64e17-d8a0-4dfd-aef8-3510f3ab10bf
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI changed the title Consolidate BitOperations downlevel polyfills under Common/src/PolyfillsConsolidate downlevel polyfills under Common/src/PolyfillsApr 1, 2026
CopilotAI requested a review from jkotasApril 1, 2026 02:11
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs Outdated
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs
…fills.cs; remove extra package refs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/3ad89f56-0cfc-41a4-9ba9-74682f13da8b
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI requested a review from jkotasApril 1, 2026 02:50
@jkotas
jkotas marked this pull request as ready for review April 1, 2026 03:27
Comment threadsrc/libraries/System.Text.Json/src/System.Text.Json.csproj Outdated

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

This PR consolidates downlevel (TargetFrameworkIdentifier != .NETCoreApp) polyfills for System.Numerics.BitOperations and Memory-based System.IO.Stream APIs into src/libraries/Common/src/Polyfills/, and updates multiple library projects to consume the shared implementations instead of library-local copies.

Changes:

  • Expanded Common’s BitOperations polyfill and switched affected libraries to reference it from Common/src/Polyfills.
  • Split stream polyfills into StreamPolyfills (no System.Memory dependencies) and StreamMemoryPolyfills (Memory-based APIs) and updated consuming projects accordingly.
  • Removed library-local polyfill sources (e.g., in System.IO.Hashing and System.Text.Encodings.Web) and updated call sites (e.g., System.Reflection.Metadata) to use BitOperations.PopCount.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.

Show a summary per file
FileDescription
src/libraries/System.Text.Json/src/System.Text.Json.csprojSwitches downlevel stream polyfill include to StreamMemoryPolyfills.
src/libraries/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csprojReplaces local BitOperations polyfill with shared BitOperationsPolyfills.
src/libraries/System.Text.Encodings.Web/src/Polyfills/System.Numerics.BitOperations.netstandard20.csDeletes library-local Log2 polyfill implementation.
src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.csReplaces BitArithmetic.CountBits usage with BitOperations.PopCount.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataSizes.csSwitches external table count calculation to BitOperations.PopCount.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/BitArithmetic.csRemoves CountBits helpers and uses BitOperations.PopCount for alignment asserts.
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csprojAdds shared BitOperationsPolyfills.cs for non-.NETCoreApp targets.
src/libraries/System.Net.ServerSentEvents/src/System.Net.ServerSentEvents.csprojSwitches downlevel stream polyfill include to StreamMemoryPolyfills.
src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csprojAdds StreamPolyfills + StreamMemoryPolyfills for downlevel targets.
src/libraries/System.IO.Pipelines/src/System.IO.Pipelines.csprojReplaces prior stream polyfill include with StreamPolyfills + StreamMemoryPolyfills.
src/libraries/System.IO.Hashing/src/System/IO/Hashing/BitOperations.csDeletes library-local RotateLeft polyfill.
src/libraries/System.IO.Hashing/src/System.IO.Hashing.csprojReferences shared BitOperationsPolyfills + StreamPolyfills for downlevel targets.
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csprojReplaces prior stream polyfill include with StreamPolyfills + StreamMemoryPolyfills.
src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.csAdds Memory-based stream polyfills (ReadAsync, Write, WriteAsync) for downlevel TFMs.
src/libraries/Common/src/Polyfills/BitOperationsPolyfills.csExpands shared BitOperations polyfill surface (Log2/PopCount/rotates/TZC).
Comments suppressed due to low confidence (2)

src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.cs:19

  • In ReadAsync(Memory) the non-array-backed path rents a buffer from ArrayPool and then calls stream.ReadAsync(...) inline. If stream.ReadAsync throws synchronously (e.g., ObjectDisposedException / argument validation) before returning a Task, the rented buffer won't be returned and will leak. Consider capturing the Task inside a try/catch and returning the rented buffer in the catch before rethrowing, then passing the Task to FinishReadAsync for the awaited path.
    src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.cs:76
  • In WriteAsync(ReadOnlyMemory) the non-array-backed path rents a buffer and then calls stream.WriteAsync(...) without guarding against synchronous exceptions. If stream.WriteAsync throws before returning a Task, the rented buffer will leak because FinishWriteAsync won't run. Capture the Task in a try/catch (or use a try/finally around the call) and ensure ArrayPool.Return happens on the synchronous-throw path as well.

… BitOperations.PopCount
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/d03d466e-d184-4531-bacb-f840ed08f950
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
ContributorAuthor

src\libraries\System.Reflection.Metadata\tests\Metadata\TagToTokenTests.cs(164,89): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'BitArithmetic' does not contain a definition for 'CountBits'
...

Fixed in commit d5a297c. Updated TagToTokenTests.cs to call BitOperations.PopCount(...) directly instead of the deleted BitArithmetic.CountBits(...), and added using System.Numerics;.

@jkotas
jkotas requested a review from EgorBoApril 1, 2026 03:52
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
ContributorAuthor

src\libraries\System.Reflection.Metadata\tests\Metadata\TagToTokenTests.cs(164,89): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'BitArithmetic' does not contain a definition for 'CountBits'
...

Already fixed in commit d5a297cTagToTokenTests.cs now calls BitOperations.PopCount(...) directly with using System.Numerics;.

…/src/Polyfills/BitOperationsPolyfills.cs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/5fb39d78-46a7-43bd-977f-845acfa9afe4
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI requested a review from jkotasApril 1, 2026 04:01
@jkotas
jkotas merged commit c24c76a into mainApr 1, 2026
90 of 97 checks passed
@jkotas
jkotas deleted the copilot/consolidate-downlevel-polyfills branch April 1, 2026 20:12
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 2, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jkotas@EgorBo
, '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

Consolidate downlevel polyfills under Common/src/Polyfills - #126391

Merged
jkotas merged 12 commits into
mainfrom
copilot/consolidate-downlevel-polyfills
Apr 1, 2026
Merged

Consolidate downlevel polyfills under Common/src/Polyfills#126391
jkotas merged 12 commits into
mainfrom
copilot/consolidate-downlevel-polyfills

Conversation

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #126287 — consolidates library-local BitOperations, Stream, StringBuilder, and Stack polyfills under src/libraries/Common/src/Polyfills/.

Changes

Common/src/Polyfills/BitOperationsPolyfills.cs

Expanded the existing polyfill (which only had RotateLeft(uint)) with the full set of methods needed by the affected libraries:

  • Log2(uint) (deBruijn-based)
  • PopCount(uint) and PopCount(ulong) (Hamming weight)
  • RotateLeft(ulong), RotateRight(uint), RotateRight(ulong)
  • TrailingZeroCount(uint) (deBruijn-based)

Common/src/Polyfills/StreamPolyfills.cs

Merged ReadExactly and CopyToAsync from the now-deleted Common/src/System/IO/StreamExtensions.netstandard.cs into the existing Stream polyfill (no System.Memory/System.Buffers dependencies).

Common/src/Polyfills/StreamMemoryPolyfills.cs(new)

New file containing the Memory-dependent stream extension methods, split out from StreamPolyfills.cs to avoid pulling in extra package references for consumers that don't need them:

  • ReadAsync(Memory<byte>, CancellationToken)
  • Write(ReadOnlyMemory<byte>)
  • WriteAsync(ReadOnlyMemory<byte>, CancellationToken)

Common/src/Polyfills/StringBuilderPolyfills.cs(new)

Moved from Common/src/System/Text/StringBuilderExtensions.cs and reworked to follow polyfill directory conventions (file-scoped namespace, extension(StringBuilder) block syntax):

  • Append(ReadOnlySpan<char>)

Common/src/Polyfills/StackPolyfills.cs(new)

Moved from System.Text.Json-local StackExtensions.netstandard.cs and reworked to follow polyfill directory conventions (file-scoped namespace, traditional extension methods matching DictionaryPolyfills.cs style for generic types):

  • TryPeek<T>(out T)
  • TryPop<T>(out T)

System.IO.Hashing

  • Removed local System/IO/Hashing/BitOperations.cs (had RotateLeft(uint/ulong))
  • Updated .csproj to reference BitOperationsPolyfills.cs and StreamPolyfills.cs

System.Text.Encodings.Web

  • Removed local Polyfills/System.Numerics.BitOperations.netstandard20.cs (had Log2)
  • Updated .csproj to reference BitOperationsPolyfills.cs

Microsoft.Bcl.Memory

  • Removed local Polyfills/System.Numerics.BitOperations.netstandard20.cs (had TrailingZeroCount, RotateLeft(uint), RotateRight(uint))
  • Updated .csproj to reference BitOperationsPolyfills.cs (which is a strict superset)

System.Reflection.Metadata

  • Deleted BitArithmetic.CountBits(uint/ulong) wrapper methods; all call sites (BitArithmetic.Align, MetadataSizes, PEHeaderBuilder) now call BitOperations.PopCount directly
  • Updated TagToTokenTests.cs to call BitOperations.PopCount directly (fixes .NETFramework build break)
  • Added BitOperationsPolyfills.cs include in .csproj for non-.NETCoreApp targets

System.Net.ServerSentEvents, System.Text.Json, Microsoft.Extensions.Logging.Console, System.Net.Http.WinHttpHandler.Functional.Tests

  • Reference StreamMemoryPolyfills.cs (use Memory-based stream methods)

System.IO.Pipelines

  • References both StreamPolyfills.cs (uses CopyToAsync) and StreamMemoryPolyfills.cs (uses ReadAsync(Memory<byte>))

System.Speech, System.ServiceModel.Syndication

  • Reference StreamPolyfills.cs only (use ReadExactly); no extra System.Memory/System.Buffers/System.Threading.Tasks.Extensions package references needed

System.Text.Json

  • Updated .csproj to reference StringBuilderPolyfills.cs and StackPolyfills.cs from Common/src/Polyfills/
  • Removed local StackExtensions.netstandard.cs

Note

This PR description was generated with the assistance of GitHub Copilot.

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

Copy link
Copy Markdown
Contributor

Note

This review was generated by Copilot.

🤖 Copilot Code Review — PR #126391

Holistic Assessment

Motivation: This PR continues the consolidation work from #126287 by merging three separate library-local BitOperations polyfill implementations (from System.IO.Hashing, System.Text.Encodings.Web, and System.Reflection.Metadata) into the shared Common/src/Polyfills/BitOperationsPolyfills.cs. The motivation is clear and well-justified — eliminating duplicate code and reducing #if NET/#else blocks.

Approach: The approach is consistent with the prior PR's pattern: centralize polyfills in a shared directory, update .csproj files to link the shared file for non-.NETCoreApp targets, and remove library-local copies. The consolidation is straightforward.

Summary: ❌ Needs Changes. The PR has a clear compilation error: a double opening brace in the Log2 method body that leaves the method unclosed. This must be fixed before merge. All other changes are clean.


Detailed Findings

❌ Compilation Error — Double { in Log2 method (BitOperationsPolyfills.cs:32-33)

Merge-blocking. The Log2 method has two consecutive opening braces but only one closing brace, creating an unbalanced brace structure that will not compile:

publicstaticintLog2(uintvalue){// ← line 32: method body opens{// ← line 33: extra block opens (from the old Log2SoftwareFallback body)// ... implementation ...returnUnsafe.AddByteOffset(...);}// ← line 50: closes only the inner block// method body is never closed!

Brace analysis of the entire file shows 8 opening braces vs 7 closing braces (final nesting depth 1, expected 0). All subsequent methods (PopCount, RotateLeft, etc.) end up syntactically nested inside the unclosed Log2 body, which is invalid C# — you cannot declare public static members inside a method.

Root cause: Commit 7ee20d2d ("Apply suggestion from @jkotas") merged the Log2 wrapper and Log2SoftwareFallback into a single method, but the replacement added a new opening brace for the merged method without removing the existing opening brace from the old Log2SoftwareFallback body.

Fix: Remove the extra { on line 33:

publicstaticintLog2(uintvalue){// No AggressiveInlining due to large method size// Has conventional contract 0->0 (Log(0) is undefined)

✅ Correctness — value | 1 removal in Log2 is safe

The original System.Text.Encodings.Web implementation called Log2SoftwareFallback(value | 1) to guarantee the 0 → 0 contract. The merged version drops the | 1. This is safe: when value = 0, the OR-shift cascade leaves it at 0, the DeBruijn multiplication yields index 0, and Log2DeBruijn[0] = 0, so Log2(0) correctly returns 0. All other inputs are unaffected since | 1 only set the lowest bit which doesn't change floor(log2(x)) for x > 0.

✅ Correctness — Algorithm implementations match BCL software fallbacks

The PopCount(uint), PopCount(ulong), TrailingZeroCount(uint), RotateLeft, and RotateRight implementations are faithful reproductions of the well-known software fallbacks (Hamming weight, DeBruijn sequences, bit rotations). These match the deleted library-local versions exactly.

✅ Consolidation — csproj changes are correct

All three .csproj files correctly:

  • Replace local file references with $(CommonPath)Polyfills\BitOperationsPolyfills.cs
  • Scope the reference to '$(TargetFrameworkIdentifier)' != '.NETCoreApp'
  • Use appropriate Link attributes for solution explorer organization

✅ Cleanup — BitArithmetic.cs#if NET removal is clean

Removing the #if NET/#else blocks in System.Reflection.Metadata's BitArithmetic.cs and unconditionally calling BitOperations.PopCount is correct — the polyfill provides the same implementation that was previously inlined under #else.

💡 Observation — Extra methods not currently consumed

The consolidated polyfill includes RotateRight(uint/ulong) and TrailingZeroCount(uint) which are not currently used by any of the three consuming libraries. This is fine for a shared polyfill (other libraries may need them in the future), but worth noting for awareness.

Generated by Code Review for issue #126391 ·

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

…rrupted BitOperationsPolyfills.cs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/7bf64e17-d8a0-4dfd-aef8-3510f3ab10bf
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI changed the title Consolidate BitOperations downlevel polyfills under Common/src/PolyfillsConsolidate downlevel polyfills under Common/src/PolyfillsApr 1, 2026
CopilotAI requested a review from jkotasApril 1, 2026 02:11
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs Outdated
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs
…fills.cs; remove extra package refs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/3ad89f56-0cfc-41a4-9ba9-74682f13da8b
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI requested a review from jkotasApril 1, 2026 02:50
@jkotas
jkotas marked this pull request as ready for review April 1, 2026 03:27
Comment threadsrc/libraries/System.Text.Json/src/System.Text.Json.csproj Outdated

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

This PR consolidates downlevel (TargetFrameworkIdentifier != .NETCoreApp) polyfills for System.Numerics.BitOperations and Memory-based System.IO.Stream APIs into src/libraries/Common/src/Polyfills/, and updates multiple library projects to consume the shared implementations instead of library-local copies.

Changes:

  • Expanded Common’s BitOperations polyfill and switched affected libraries to reference it from Common/src/Polyfills.
  • Split stream polyfills into StreamPolyfills (no System.Memory dependencies) and StreamMemoryPolyfills (Memory-based APIs) and updated consuming projects accordingly.
  • Removed library-local polyfill sources (e.g., in System.IO.Hashing and System.Text.Encodings.Web) and updated call sites (e.g., System.Reflection.Metadata) to use BitOperations.PopCount.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.

Show a summary per file
FileDescription
src/libraries/System.Text.Json/src/System.Text.Json.csprojSwitches downlevel stream polyfill include to StreamMemoryPolyfills.
src/libraries/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csprojReplaces local BitOperations polyfill with shared BitOperationsPolyfills.
src/libraries/System.Text.Encodings.Web/src/Polyfills/System.Numerics.BitOperations.netstandard20.csDeletes library-local Log2 polyfill implementation.
src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.csReplaces BitArithmetic.CountBits usage with BitOperations.PopCount.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataSizes.csSwitches external table count calculation to BitOperations.PopCount.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/BitArithmetic.csRemoves CountBits helpers and uses BitOperations.PopCount for alignment asserts.
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csprojAdds shared BitOperationsPolyfills.cs for non-.NETCoreApp targets.
src/libraries/System.Net.ServerSentEvents/src/System.Net.ServerSentEvents.csprojSwitches downlevel stream polyfill include to StreamMemoryPolyfills.
src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csprojAdds StreamPolyfills + StreamMemoryPolyfills for downlevel targets.
src/libraries/System.IO.Pipelines/src/System.IO.Pipelines.csprojReplaces prior stream polyfill include with StreamPolyfills + StreamMemoryPolyfills.
src/libraries/System.IO.Hashing/src/System/IO/Hashing/BitOperations.csDeletes library-local RotateLeft polyfill.
src/libraries/System.IO.Hashing/src/System.IO.Hashing.csprojReferences shared BitOperationsPolyfills + StreamPolyfills for downlevel targets.
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csprojReplaces prior stream polyfill include with StreamPolyfills + StreamMemoryPolyfills.
src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.csAdds Memory-based stream polyfills (ReadAsync, Write, WriteAsync) for downlevel TFMs.
src/libraries/Common/src/Polyfills/BitOperationsPolyfills.csExpands shared BitOperations polyfill surface (Log2/PopCount/rotates/TZC).
Comments suppressed due to low confidence (2)

src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.cs:19

  • In ReadAsync(Memory) the non-array-backed path rents a buffer from ArrayPool and then calls stream.ReadAsync(...) inline. If stream.ReadAsync throws synchronously (e.g., ObjectDisposedException / argument validation) before returning a Task, the rented buffer won't be returned and will leak. Consider capturing the Task inside a try/catch and returning the rented buffer in the catch before rethrowing, then passing the Task to FinishReadAsync for the awaited path.
    src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.cs:76
  • In WriteAsync(ReadOnlyMemory) the non-array-backed path rents a buffer and then calls stream.WriteAsync(...) without guarding against synchronous exceptions. If stream.WriteAsync throws before returning a Task, the rented buffer will leak because FinishWriteAsync won't run. Capture the Task in a try/catch (or use a try/finally around the call) and ensure ArrayPool.Return happens on the synchronous-throw path as well.

… BitOperations.PopCount
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/d03d466e-d184-4531-bacb-f840ed08f950
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
ContributorAuthor

src\libraries\System.Reflection.Metadata\tests\Metadata\TagToTokenTests.cs(164,89): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'BitArithmetic' does not contain a definition for 'CountBits'
...

Fixed in commit d5a297c. Updated TagToTokenTests.cs to call BitOperations.PopCount(...) directly instead of the deleted BitArithmetic.CountBits(...), and added using System.Numerics;.

@jkotas
jkotas requested a review from EgorBoApril 1, 2026 03:52
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
ContributorAuthor

src\libraries\System.Reflection.Metadata\tests\Metadata\TagToTokenTests.cs(164,89): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'BitArithmetic' does not contain a definition for 'CountBits'
...

Already fixed in commit d5a297cTagToTokenTests.cs now calls BitOperations.PopCount(...) directly with using System.Numerics;.

…/src/Polyfills/BitOperationsPolyfills.cs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/5fb39d78-46a7-43bd-977f-845acfa9afe4
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI requested a review from jkotasApril 1, 2026 04:01
@jkotas
jkotas merged commit c24c76a into mainApr 1, 2026
90 of 97 checks passed
@jkotas
jkotas deleted the copilot/consolidate-downlevel-polyfills branch April 1, 2026 20:12
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 2, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jkotas@EgorBo
, '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

Consolidate downlevel polyfills under Common/src/Polyfills - #126391

Merged
jkotas merged 12 commits into
mainfrom
copilot/consolidate-downlevel-polyfills
Apr 1, 2026
Merged

Consolidate downlevel polyfills under Common/src/Polyfills#126391
jkotas merged 12 commits into
mainfrom
copilot/consolidate-downlevel-polyfills

Conversation

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #126287 — consolidates library-local BitOperations, Stream, StringBuilder, and Stack polyfills under src/libraries/Common/src/Polyfills/.

Changes

Common/src/Polyfills/BitOperationsPolyfills.cs

Expanded the existing polyfill (which only had RotateLeft(uint)) with the full set of methods needed by the affected libraries:

  • Log2(uint) (deBruijn-based)
  • PopCount(uint) and PopCount(ulong) (Hamming weight)
  • RotateLeft(ulong), RotateRight(uint), RotateRight(ulong)
  • TrailingZeroCount(uint) (deBruijn-based)

Common/src/Polyfills/StreamPolyfills.cs

Merged ReadExactly and CopyToAsync from the now-deleted Common/src/System/IO/StreamExtensions.netstandard.cs into the existing Stream polyfill (no System.Memory/System.Buffers dependencies).

Common/src/Polyfills/StreamMemoryPolyfills.cs(new)

New file containing the Memory-dependent stream extension methods, split out from StreamPolyfills.cs to avoid pulling in extra package references for consumers that don't need them:

  • ReadAsync(Memory<byte>, CancellationToken)
  • Write(ReadOnlyMemory<byte>)
  • WriteAsync(ReadOnlyMemory<byte>, CancellationToken)

Common/src/Polyfills/StringBuilderPolyfills.cs(new)

Moved from Common/src/System/Text/StringBuilderExtensions.cs and reworked to follow polyfill directory conventions (file-scoped namespace, extension(StringBuilder) block syntax):

  • Append(ReadOnlySpan<char>)

Common/src/Polyfills/StackPolyfills.cs(new)

Moved from System.Text.Json-local StackExtensions.netstandard.cs and reworked to follow polyfill directory conventions (file-scoped namespace, traditional extension methods matching DictionaryPolyfills.cs style for generic types):

  • TryPeek<T>(out T)
  • TryPop<T>(out T)

System.IO.Hashing

  • Removed local System/IO/Hashing/BitOperations.cs (had RotateLeft(uint/ulong))
  • Updated .csproj to reference BitOperationsPolyfills.cs and StreamPolyfills.cs

System.Text.Encodings.Web

  • Removed local Polyfills/System.Numerics.BitOperations.netstandard20.cs (had Log2)
  • Updated .csproj to reference BitOperationsPolyfills.cs

Microsoft.Bcl.Memory

  • Removed local Polyfills/System.Numerics.BitOperations.netstandard20.cs (had TrailingZeroCount, RotateLeft(uint), RotateRight(uint))
  • Updated .csproj to reference BitOperationsPolyfills.cs (which is a strict superset)

System.Reflection.Metadata

  • Deleted BitArithmetic.CountBits(uint/ulong) wrapper methods; all call sites (BitArithmetic.Align, MetadataSizes, PEHeaderBuilder) now call BitOperations.PopCount directly
  • Updated TagToTokenTests.cs to call BitOperations.PopCount directly (fixes .NETFramework build break)
  • Added BitOperationsPolyfills.cs include in .csproj for non-.NETCoreApp targets

System.Net.ServerSentEvents, System.Text.Json, Microsoft.Extensions.Logging.Console, System.Net.Http.WinHttpHandler.Functional.Tests

  • Reference StreamMemoryPolyfills.cs (use Memory-based stream methods)

System.IO.Pipelines

  • References both StreamPolyfills.cs (uses CopyToAsync) and StreamMemoryPolyfills.cs (uses ReadAsync(Memory<byte>))

System.Speech, System.ServiceModel.Syndication

  • Reference StreamPolyfills.cs only (use ReadExactly); no extra System.Memory/System.Buffers/System.Threading.Tasks.Extensions package references needed

System.Text.Json

  • Updated .csproj to reference StringBuilderPolyfills.cs and StackPolyfills.cs from Common/src/Polyfills/
  • Removed local StackExtensions.netstandard.cs

Note

This PR description was generated with the assistance of GitHub Copilot.

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

Copy link
Copy Markdown
Contributor

Note

This review was generated by Copilot.

🤖 Copilot Code Review — PR #126391

Holistic Assessment

Motivation: This PR continues the consolidation work from #126287 by merging three separate library-local BitOperations polyfill implementations (from System.IO.Hashing, System.Text.Encodings.Web, and System.Reflection.Metadata) into the shared Common/src/Polyfills/BitOperationsPolyfills.cs. The motivation is clear and well-justified — eliminating duplicate code and reducing #if NET/#else blocks.

Approach: The approach is consistent with the prior PR's pattern: centralize polyfills in a shared directory, update .csproj files to link the shared file for non-.NETCoreApp targets, and remove library-local copies. The consolidation is straightforward.

Summary: ❌ Needs Changes. The PR has a clear compilation error: a double opening brace in the Log2 method body that leaves the method unclosed. This must be fixed before merge. All other changes are clean.


Detailed Findings

❌ Compilation Error — Double { in Log2 method (BitOperationsPolyfills.cs:32-33)

Merge-blocking. The Log2 method has two consecutive opening braces but only one closing brace, creating an unbalanced brace structure that will not compile:

publicstaticintLog2(uintvalue){// ← line 32: method body opens{// ← line 33: extra block opens (from the old Log2SoftwareFallback body)// ... implementation ...returnUnsafe.AddByteOffset(...);}// ← line 50: closes only the inner block// method body is never closed!

Brace analysis of the entire file shows 8 opening braces vs 7 closing braces (final nesting depth 1, expected 0). All subsequent methods (PopCount, RotateLeft, etc.) end up syntactically nested inside the unclosed Log2 body, which is invalid C# — you cannot declare public static members inside a method.

Root cause: Commit 7ee20d2d ("Apply suggestion from @jkotas") merged the Log2 wrapper and Log2SoftwareFallback into a single method, but the replacement added a new opening brace for the merged method without removing the existing opening brace from the old Log2SoftwareFallback body.

Fix: Remove the extra { on line 33:

publicstaticintLog2(uintvalue){// No AggressiveInlining due to large method size// Has conventional contract 0->0 (Log(0) is undefined)

✅ Correctness — value | 1 removal in Log2 is safe

The original System.Text.Encodings.Web implementation called Log2SoftwareFallback(value | 1) to guarantee the 0 → 0 contract. The merged version drops the | 1. This is safe: when value = 0, the OR-shift cascade leaves it at 0, the DeBruijn multiplication yields index 0, and Log2DeBruijn[0] = 0, so Log2(0) correctly returns 0. All other inputs are unaffected since | 1 only set the lowest bit which doesn't change floor(log2(x)) for x > 0.

✅ Correctness — Algorithm implementations match BCL software fallbacks

The PopCount(uint), PopCount(ulong), TrailingZeroCount(uint), RotateLeft, and RotateRight implementations are faithful reproductions of the well-known software fallbacks (Hamming weight, DeBruijn sequences, bit rotations). These match the deleted library-local versions exactly.

✅ Consolidation — csproj changes are correct

All three .csproj files correctly:

  • Replace local file references with $(CommonPath)Polyfills\BitOperationsPolyfills.cs
  • Scope the reference to '$(TargetFrameworkIdentifier)' != '.NETCoreApp'
  • Use appropriate Link attributes for solution explorer organization

✅ Cleanup — BitArithmetic.cs#if NET removal is clean

Removing the #if NET/#else blocks in System.Reflection.Metadata's BitArithmetic.cs and unconditionally calling BitOperations.PopCount is correct — the polyfill provides the same implementation that was previously inlined under #else.

💡 Observation — Extra methods not currently consumed

The consolidated polyfill includes RotateRight(uint/ulong) and TrailingZeroCount(uint) which are not currently used by any of the three consuming libraries. This is fine for a shared polyfill (other libraries may need them in the future), but worth noting for awareness.

Generated by Code Review for issue #126391 ·

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

…rrupted BitOperationsPolyfills.cs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/7bf64e17-d8a0-4dfd-aef8-3510f3ab10bf
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI changed the title Consolidate BitOperations downlevel polyfills under Common/src/PolyfillsConsolidate downlevel polyfills under Common/src/PolyfillsApr 1, 2026
CopilotAI requested a review from jkotasApril 1, 2026 02:11
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs Outdated
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs
…fills.cs; remove extra package refs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/3ad89f56-0cfc-41a4-9ba9-74682f13da8b
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI requested a review from jkotasApril 1, 2026 02:50
@jkotas
jkotas marked this pull request as ready for review April 1, 2026 03:27
Comment threadsrc/libraries/System.Text.Json/src/System.Text.Json.csproj Outdated

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

This PR consolidates downlevel (TargetFrameworkIdentifier != .NETCoreApp) polyfills for System.Numerics.BitOperations and Memory-based System.IO.Stream APIs into src/libraries/Common/src/Polyfills/, and updates multiple library projects to consume the shared implementations instead of library-local copies.

Changes:

  • Expanded Common’s BitOperations polyfill and switched affected libraries to reference it from Common/src/Polyfills.
  • Split stream polyfills into StreamPolyfills (no System.Memory dependencies) and StreamMemoryPolyfills (Memory-based APIs) and updated consuming projects accordingly.
  • Removed library-local polyfill sources (e.g., in System.IO.Hashing and System.Text.Encodings.Web) and updated call sites (e.g., System.Reflection.Metadata) to use BitOperations.PopCount.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated no comments.

Show a summary per file
FileDescription
src/libraries/System.Text.Json/src/System.Text.Json.csprojSwitches downlevel stream polyfill include to StreamMemoryPolyfills.
src/libraries/System.Text.Encodings.Web/src/System.Text.Encodings.Web.csprojReplaces local BitOperations polyfill with shared BitOperationsPolyfills.
src/libraries/System.Text.Encodings.Web/src/Polyfills/System.Numerics.BitOperations.netstandard20.csDeletes library-local Log2 polyfill implementation.
src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaderBuilder.csReplaces BitArithmetic.CountBits usage with BitOperations.PopCount.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/Ecma335/MetadataSizes.csSwitches external table count calculation to BitOperations.PopCount.
src/libraries/System.Reflection.Metadata/src/System/Reflection/Internal/Utilities/BitArithmetic.csRemoves CountBits helpers and uses BitOperations.PopCount for alignment asserts.
src/libraries/System.Reflection.Metadata/src/System.Reflection.Metadata.csprojAdds shared BitOperationsPolyfills.cs for non-.NETCoreApp targets.
src/libraries/System.Net.ServerSentEvents/src/System.Net.ServerSentEvents.csprojSwitches downlevel stream polyfill include to StreamMemoryPolyfills.
src/libraries/System.Net.Http.WinHttpHandler/tests/FunctionalTests/System.Net.Http.WinHttpHandler.Functional.Tests.csprojAdds StreamPolyfills + StreamMemoryPolyfills for downlevel targets.
src/libraries/System.IO.Pipelines/src/System.IO.Pipelines.csprojReplaces prior stream polyfill include with StreamPolyfills + StreamMemoryPolyfills.
src/libraries/System.IO.Hashing/src/System/IO/Hashing/BitOperations.csDeletes library-local RotateLeft polyfill.
src/libraries/System.IO.Hashing/src/System.IO.Hashing.csprojReferences shared BitOperationsPolyfills + StreamPolyfills for downlevel targets.
src/libraries/Microsoft.Extensions.Logging.Console/src/Microsoft.Extensions.Logging.Console.csprojReplaces prior stream polyfill include with StreamPolyfills + StreamMemoryPolyfills.
src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.csAdds Memory-based stream polyfills (ReadAsync, Write, WriteAsync) for downlevel TFMs.
src/libraries/Common/src/Polyfills/BitOperationsPolyfills.csExpands shared BitOperations polyfill surface (Log2/PopCount/rotates/TZC).
Comments suppressed due to low confidence (2)

src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.cs:19

  • In ReadAsync(Memory) the non-array-backed path rents a buffer from ArrayPool and then calls stream.ReadAsync(...) inline. If stream.ReadAsync throws synchronously (e.g., ObjectDisposedException / argument validation) before returning a Task, the rented buffer won't be returned and will leak. Consider capturing the Task inside a try/catch and returning the rented buffer in the catch before rethrowing, then passing the Task to FinishReadAsync for the awaited path.
    src/libraries/Common/src/Polyfills/StreamMemoryPolyfills.cs:76
  • In WriteAsync(ReadOnlyMemory) the non-array-backed path rents a buffer and then calls stream.WriteAsync(...) without guarding against synchronous exceptions. If stream.WriteAsync throws before returning a Task, the rented buffer will leak because FinishWriteAsync won't run. Capture the Task in a try/catch (or use a try/finally around the call) and ensure ArrayPool.Return happens on the synchronous-throw path as well.

… BitOperations.PopCount
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/d03d466e-d184-4531-bacb-f840ed08f950
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
ContributorAuthor

src\libraries\System.Reflection.Metadata\tests\Metadata\TagToTokenTests.cs(164,89): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'BitArithmetic' does not contain a definition for 'CountBits'
...

Fixed in commit d5a297c. Updated TagToTokenTests.cs to call BitOperations.PopCount(...) directly instead of the deleted BitArithmetic.CountBits(...), and added using System.Numerics;.

@jkotas
jkotas requested a review from EgorBoApril 1, 2026 03:52
Comment threadsrc/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs

CopilotAI commented Apr 1, 2026

Copy link
Copy Markdown
ContributorAuthor

src\libraries\System.Reflection.Metadata\tests\Metadata\TagToTokenTests.cs(164,89): error CS0117: (NETCORE_ENGINEERING_TELEMETRY=Build) 'BitArithmetic' does not contain a definition for 'CountBits'
...

Already fixed in commit d5a297cTagToTokenTests.cs now calls BitOperations.PopCount(...) directly with using System.Numerics;.

…/src/Polyfills/BitOperationsPolyfills.cs
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/5fb39d78-46a7-43bd-977f-845acfa9afe4
Co-authored-by: jkotas <6668460+jkotas@users.noreply.github.com>
CopilotAI requested a review from jkotasApril 1, 2026 04:01
@jkotas
jkotas merged commit c24c76a into mainApr 1, 2026
90 of 97 checks passed
@jkotas
jkotas deleted the copilot/consolidate-downlevel-polyfills branch April 1, 2026 20:12
@github-actionsgithub-actionsBot locked and limited conversation to collaborators May 2, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@jkotas@EgorBo