Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a5b5bbc
move all pollyfills to a common folder
EgorBo Mar 30, 2026
878040d
Revert OperatingSystem polyfills, expand Array.MaxLength usage, inlin…
EgorBo Mar 30, 2026
fd718d6
Move Immutable Polyfills.cs to Common/src/Polyfills/
EgorBo Mar 30, 2026
8fbba08
Merge remote-tracking branch 'origin/main' into common-pollyfills
EgorBo Mar 30, 2026
f5429ba
Move Reflection.Metadata Polyfills.cs to Common/src/Polyfills/
EgorBo Mar 30, 2026
cf145c9
Apply existing polyfills to more libraries
EgorBo Mar 30, 2026
672bbab
Address PR feedback
EgorBo Mar 30, 2026
b7fd6b1
Add RuntimeHelpersPolyfills, clean up STJ #if NET blocks
EgorBo Mar 30, 2026
dab7861
Address PR review feedback
EgorBo Mar 30, 2026
52771e2
Revert SimpleConsoleFormatter.cs, use collection literal for char span
EgorBo Mar 30, 2026
e4fefc4
Revert DiagnosticSource polyfill change (net481 CI uses VS MSBuild)
EgorBo Mar 30, 2026
4e9206c
Merge remote-tracking branch 'origin/main' into common-pollyfills
EgorBo Mar 30, 2026
ea731b6
FB
EgorBo Mar 30, 2026
922b086
Update src/libraries/Common/src/Polyfills/CollectionsPolyfills.cs
EgorBo Mar 30, 2026
313d26d
Merge branch 'common-pollyfills' of https://github.com/dotnet/runtime…
EgorBo Mar 30, 2026
aaabccd
Address review feedback: fix usings, remove dead DictionaryPolyfills
EgorBo Mar 30, 2026
87571d4
Move DictionaryExtensions.cs to Common/src/Polyfills/DictionaryPolyfi…
EgorBo Mar 30, 2026
8afd56b
Use file-scoped namespaces in Polyfills; unrevert DiagnosticSource po…
EgorBo Mar 30, 2026
8771018
Fix SA1518: add trailing newline to all Polyfills files
EgorBo Mar 31, 2026
721b99f
Fix net481 CI: add DoublePolyfills to DiagnosticSource test project
EgorBo Mar 31, 2026
0f64157
Move EncodingPolyfills.cs to Common/src/Polyfills/ directory
Copilot Mar 31, 2026
f0bf71e
Cache ProcessId in EnvironmentPolyfills to avoid repeated Process obj…
Copilot Mar 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/libraries/Common/src/Polyfills/ArrayPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System;

/// <summary>Provides downlevel polyfills for static members on <see cref="Array"/>.</summary>
internal static class ArrayPolyfills
{
extension(Array)
{
public static int MaxLength => 0x7FFFFFC7;
}
}
26 changes: 26 additions & 0 deletions src/libraries/Common/src/Polyfills/BitArrayPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections;

namespace System.Collections;

/// <summary>Provides downlevel polyfills for instance methods on <see cref="BitArray"/>.</summary>
internal static class BitArrayPolyfills
{
extension(BitArray bitArray)
{
public bool HasAllSet()
{
for (int i = 0; i < bitArray.Count; i++)
{
if (!bitArray[i])
{
return false;
}
}

return true;
}
}
}
27 changes: 27 additions & 0 deletions src/libraries/Common/src/Polyfills/BitConverterPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System;

/// <summary>Provides downlevel polyfills for static methods on <see cref="BitConverter"/>.</summary>
internal static class BitConverterPolyfills
{
extension(BitConverter)
{
public static uint SingleToUInt32Bits(float value)
{
unsafe
{
return *(uint*)&value;
}
}

public static ulong DoubleToUInt64Bits(double value)
{
unsafe
{
return *(ulong*)&value;
}
}
}
}
12 changes: 12 additions & 0 deletions src/libraries/Common/src/Polyfills/BitOperationsPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;

namespace System.Numerics;

internal static class BitOperations
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint RotateLeft(uint value, int offset) => (value << offset) | (value >> (32 - offset));
}
14 changes: 14 additions & 0 deletions src/libraries/Common/src/Polyfills/CollectionsPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Collections.Generic;

/// <summary>Provides downlevel polyfills for <see cref="KeyValuePair{TKey, TValue}"/>.</summary>
internal static class KeyValuePairPolyfills
{
public static void Deconstruct<TKey, TValue>(this KeyValuePair<TKey, TValue> source, out TKey key, out TValue value)
{
key = source.Key;
value = source.Value;
}
}
14 changes: 14 additions & 0 deletions src/libraries/Common/src/Polyfills/DateTimeOffsetPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System;

/// <summary>Provides downlevel polyfills for instance members on <see cref="DateTimeOffset"/>.</summary>
internal static class DateTimeOffsetPolyfills
{
extension(DateTimeOffset value)
{
public int TotalOffsetMinutes =>
(int)(value.Offset.Ticks / TimeSpan.TicksPerMinute);
}
}
19 changes: 19 additions & 0 deletions src/libraries/Common/src/Polyfills/DictionaryPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System.Collections.Generic;

/// <summary>Provides downlevel polyfills for instance methods on <see cref="IDictionary{TKey, TValue}"/>.</summary>
internal static class DictionaryPolyfills
{
public static bool TryAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
{
if (!dictionary.ContainsKey(key))
{
dictionary.Add(key, value);
return true;
}

return false;
}
}
20 changes: 20 additions & 0 deletions src/libraries/Common/src/Polyfills/DoublePolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;

namespace System;

/// <summary>Provides downlevel polyfills for static methods on <see cref="double"/>.</summary>
internal static class DoublePolyfills
{
extension(double)
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsFinite(double d)
{
long bits = BitConverter.DoubleToInt64Bits(d);
return ((ulong)~bits & 0x7FF0_0000_0000_0000UL) != 0;
}
}
}
81 changes: 81 additions & 0 deletions src/libraries/Common/src/Polyfills/EncodingPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace System.Text;

/// <summary>Provides downlevel polyfills for span-based Encoding APIs.</summary>
internal static class EncodingPolyfills
{
public static unsafe int GetByteCount(this Encoding encoding, ReadOnlySpan<char> chars)
{
fixed (char* charsPtr = &GetNonNullPinnableReference(chars))
{
return encoding.GetByteCount(charsPtr, chars.Length);
}
}

public static unsafe int GetCharCount(this Encoding encoding, ReadOnlySpan<byte> bytes)
{
fixed (byte* bytesPtr = &GetNonNullPinnableReference(bytes))
{
return encoding.GetCharCount(bytesPtr, bytes.Length);
}
}

public static int GetBytes(this Encoding encoding, string str, Span<byte> bytes)
{
return GetBytes(encoding, str.AsSpan(), bytes);
}

public static unsafe int GetBytes(this Encoding encoding, ReadOnlySpan<char> chars, Span<byte> bytes)
{
fixed (char* charsPtr = &GetNonNullPinnableReference(chars))
fixed (byte* bytesPtr = &GetNonNullPinnableReference(bytes))
{
return encoding.GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length);
}
}

public static unsafe int GetChars(this Encoding encoding, ReadOnlySpan<byte> bytes, Span<char> chars)
{
fixed (byte* bytesPtr = &GetNonNullPinnableReference(bytes))
fixed (char* charsPtr = &GetNonNullPinnableReference(chars))
{
return encoding.GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length);
}
}

public static unsafe string GetString(this Encoding encoding, ReadOnlySpan<byte> bytes)
{
fixed (byte* bytesPtr = &GetNonNullPinnableReference(bytes))
{
return encoding.GetString(bytesPtr, bytes.Length);
}
}

public static bool TryGetChars(this Encoding encoding, ReadOnlySpan<byte> bytes, Span<char> chars, out int charsWritten)
{
int charCount = encoding.GetCharCount(bytes);

if (charCount > chars.Length)
{
charsWritten = 0;
return false;
}

charsWritten = encoding.GetChars(bytes, chars);
Debug.Assert(charCount == charsWritten);
return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe ref readonly T GetNonNullPinnableReference<T>(ReadOnlySpan<T> buffer)
{
// Based on the internal implementation from MemoryMarshal.
return ref buffer.Length != 0 ? ref MemoryMarshal.GetReference(buffer) : ref Unsafe.AsRef<T>((void*)1);
}
}
29 changes: 29 additions & 0 deletions src/libraries/Common/src/Polyfills/EnvironmentPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace System;

/// <summary>Provides downlevel polyfills for static members on <see cref="Environment"/>.</summary>
internal static class EnvironmentPolyfills
{
private static int s_processId;

extension(Environment)
{
public static int ProcessId
{
get
{
int processId = s_processId;
if (processId == 0)
{
using Process currentProcess = Process.GetCurrentProcess();
s_processId = processId = currentProcess.Id;
}
return processId;
}
}
}
}
33 changes: 33 additions & 0 deletions src/libraries/Common/src/Polyfills/GuidPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace System;

/// <summary>Provides downlevel polyfills for instance methods on <see cref="Guid"/>.</summary>
internal static class GuidPolyfills
{
extension(Guid self)
{
public bool TryWriteBytes(Span<byte> destination)
{
if (destination.Length < 16)
return false;

ref Guid selfRef = ref Unsafe.AsRef(in self);
if (BitConverter.IsLittleEndian)
{
MemoryMarshal.Write(destination, ref selfRef);
}
else
{
// slower path for BigEndian
self.ToByteArray().AsSpan().CopyTo(destination);
}

return true;
}
}
}
24 changes: 24 additions & 0 deletions src/libraries/Common/src/Polyfills/HashCodePolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;

namespace System;

/// <summary>Provides downlevel polyfills for instance methods on <see cref="HashCode"/>.</summary>
internal static class HashCodePolyfills
{
public static void AddBytes(this ref HashCode hashCode, ReadOnlySpan<byte> value)
{
while (value.Length >= sizeof(int))
{
hashCode.Add(MemoryMarshal.Read<int>(value));
value = value.Slice(sizeof(int));
}

foreach (byte b in value)
{
hashCode.Add(b);
}
}
}
34 changes: 34 additions & 0 deletions src/libraries/Common/src/Polyfills/RuntimeHelpersPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;

namespace System.Runtime.CompilerServices;

/// <summary>Provides downlevel polyfills for static methods on <see cref="RuntimeHelpers"/>.</summary>
internal static class RuntimeHelpersPolyfills
{
extension(RuntimeHelpers)
{
public static bool TryEnsureSufficientExecutionStack()
{
try
{
RuntimeHelpers.EnsureSufficientExecutionStack();
return true;
}
catch
{
return false;
}
}

public static object GetUninitializedObject(Type type)
{
#pragma warning disable SYSLIB0050 // FormatterServices.GetUninitializedObject is obsolete
return System.Runtime.Serialization.FormatterServices.GetUninitializedObject(type);
#pragma warning restore SYSLIB0050
}
}
}
20 changes: 20 additions & 0 deletions src/libraries/Common/src/Polyfills/SinglePolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;

namespace System;

/// <summary>Provides downlevel polyfills for static methods on <see cref="float"/>.</summary>
internal static class SinglePolyfills
{
extension(float)
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool IsFinite(float f)
{
uint bits = *(uint*)&f;
return (~bits & 0x7F80_0000U) != 0;
}
}
}
Loading
Loading