Restore Polyfills.Polyfill.Deconstruct for binary compatibility (#9710) - #9738

Closed
Amaury Levé (Evangelink) wants to merge 2 commits into
mainfrom
dev/amauryleve/fix-polyfill-deconstruct-compat-9710
Closed

Restore Polyfills.Polyfill.Deconstruct for binary compatibility (#9710)#9738
Amaury Levé (Evangelink) wants to merge 2 commits into
mainfrom
dev/amauryleve/fix-polyfill-deconstruct-compat-9710

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#9710.

Problem

Upgrading to MTP 2.3.0 can throw at runtime:

System.MissingMethodException: Method not found: 'Void Polyfills.Polyfill.Deconstruct(System.Collections.Generic.KeyValuePair`2<!!0,!!1>, !!0 ByRef, !!1 ByRef)'.
at Microsoft.Testing.Extensions.Telemetry.AppInsightsProvider.<IngestLoopAsync>d__18.MoveNext()

Root cause

Older Microsoft.Testing.* extension assemblies (built before the Polyfill package switched to [Embedded] types) were compiled against Polyfills.Polyfill.Deconstruct(KeyValuePair<,>, out, out) and — through InternalsVisibleTo — bound that call to the copy declared in the coreMicrosoft.Testing.Platform assembly. When Polyfill was dropped (#7726), that method disappeared, so pairing a newer core (2.3.0) with an older extension (e.g. an old Microsoft.Testing.Extensions.Telemetry pulled in transitively, as happens with xunit.v3.mtp-v2) fails with MissingMethodException.

Fix

Re-add the single Deconstruct extension onto the existing[Embedded] internal static partial class Polyfills.Polyfill that core already ships (in ProcessExtensions.cs). Core still grants InternalsVisibleTo to those extension assemblies under the same key, so this restores the runtime binding for already-shipped old extensions — without re-exposing the polyfill to source consumers (it stays [Embedded]). Current source is unaffected: nothing deconstructs a KeyValuePair anymore, and on .NET the framework instance Deconstruct still wins.

The method is marked [Obsolete] with a message stating it must be removed in the next major version of Microsoft.Testing.Platform. It's scoped to the method (not the whole Polyfill type) because that partial type also hosts WaitForExitAsync, which core still uses on netstandard2.0.

The consumer-side workaround from the issue (pinning Microsoft.Testing.Extensions.Telemetry to the core version) still applies; this change is a binary-compat safety net.

Verification

  • PE-metadata probe confirms Polyfills.Polyfill.Deconstruct is emitted in the compiled net8.0 core DLL.
  • Full build.cmd -c Debug succeeds with 0 warnings / 0 errors across all TFMs (net462/472/48, net8.0, net9.0, netstandard2.0, uap).

Note

Reintroducing removed Polyfill surface is a design call — happy to drop this in favor of a packaging-only solution if maintainers prefer.

Older Microsoft.Testing extension assemblies (built before Polyfill adopted [Embedded]) reference Polyfills.Polyfill.Deconstruct in the core Microsoft.Testing.Platform assembly via InternalsVisibleTo. Dropping the Polyfill package removed that method, so pairing a newer core with an older extension (e.g. an old Telemetry) throws MissingMethodException at runtime. Re-add the single Deconstruct extension on the existing [Embedded] Polyfill partial type to restore the runtime binding without re-exposing the polyfill to source consumers.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…#9710)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 8, 2026 14:36
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 8, 2026 14:36

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 restores Polyfills.Polyfill.Deconstruct(KeyValuePair<,>, out, out) in the embedded Polyfills.Polyfill type to preserve runtime binary compatibility when newer Microsoft.Testing.Platform cores are paired with older Microsoft.Testing.* extension assemblies.

Changes:

  • Reintroduces a single Deconstruct<TKey,TValue> extension method on Polyfills.Polyfill as a binary-compatibility shim.
  • Marks the shim as [Obsolete] with guidance to remove it in the next major version.
Show a summary per file
FileDescription
src/Polyfills/Polyfill.csAdds back Polyfills.Polyfill.Deconstruct to prevent MissingMethodException with older extension binaries.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@github-actionsgithub-actionsBot 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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

✅ 22/22 dimensions clean — no findings.

Summary: Well-scoped binary-compatibility shim that restores the exact method signature old extension assemblies expect (Void Polyfills.Polyfill.Deconstruct(KeyValuePair<TKey,TValue>, TKey&, TValue&)). The fix is correct because:

  • The [Embedded] attribute on the partial declaration in ProcessExtensions.cs covers the whole type — no attribute needed on this new partial.
  • InternalsVisibleTo from Microsoft.Testing.Platform.csproj already grants access to the affected extension assemblies (Telemetry, etc.).
  • On .NET 5+, the framework's instance Deconstruct wins for source resolution; this extension is only invoked by pre-compiled old binaries via method token.
  • The [Obsolete] message is clear, actionable, and references the tracking issue.
  • No public API surface change — the class is internal.
  • The unconditional compilation (no #if guard) is intentional and correct: old extensions target both .NET and .NET Framework, so the shim must be emitted on all TFMs.

auto-merge was automatically disabled July 8, 2026 15:39

Pull request was closed

@Evangelink

Copy link
Copy Markdown
MemberAuthor

Closing as superseded by #9739, which fixes the same issue (#9710) but more completely:

  • Restores the full set of Polyfills.Polyfill members old 2.x extensions reference (Deconstruct, Contains×2, AppendJoin×2, CopyToAsync, SaveAsync), not just Deconstruct — so every old 2.x extension loads correctly on .NET Framework, not only Telemetry.
  • Guards the shim to #if !NETCOREAPP so it only lands in the netstandard2.0 asset that's actually loaded on .NET Framework.
  • Adds a reproducing acceptance test (TelemetryForwardCompatibilityTests) using the oldest supported 2.0.0 extension with telemetry enabled — verified red without the shim and green with it.

One thing worth carrying over from here: the [Obsolete] "remove in next major" annotation. I'll port that intent onto #9739.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.MissingMethodException after upgrade to 2.3.0

2 participants

@Evangelink
, '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

Restore Polyfills.Polyfill.Deconstruct for binary compatibility (#9710) - #9738

Closed
Amaury Levé (Evangelink) wants to merge 2 commits into
mainfrom
dev/amauryleve/fix-polyfill-deconstruct-compat-9710
Closed

Restore Polyfills.Polyfill.Deconstruct for binary compatibility (#9710)#9738
Amaury Levé (Evangelink) wants to merge 2 commits into
mainfrom
dev/amauryleve/fix-polyfill-deconstruct-compat-9710

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#9710.

Problem

Upgrading to MTP 2.3.0 can throw at runtime:

System.MissingMethodException: Method not found: 'Void Polyfills.Polyfill.Deconstruct(System.Collections.Generic.KeyValuePair`2<!!0,!!1>, !!0 ByRef, !!1 ByRef)'.
at Microsoft.Testing.Extensions.Telemetry.AppInsightsProvider.<IngestLoopAsync>d__18.MoveNext()

Root cause

Older Microsoft.Testing.* extension assemblies (built before the Polyfill package switched to [Embedded] types) were compiled against Polyfills.Polyfill.Deconstruct(KeyValuePair<,>, out, out) and — through InternalsVisibleTo — bound that call to the copy declared in the coreMicrosoft.Testing.Platform assembly. When Polyfill was dropped (#7726), that method disappeared, so pairing a newer core (2.3.0) with an older extension (e.g. an old Microsoft.Testing.Extensions.Telemetry pulled in transitively, as happens with xunit.v3.mtp-v2) fails with MissingMethodException.

Fix

Re-add the single Deconstruct extension onto the existing[Embedded] internal static partial class Polyfills.Polyfill that core already ships (in ProcessExtensions.cs). Core still grants InternalsVisibleTo to those extension assemblies under the same key, so this restores the runtime binding for already-shipped old extensions — without re-exposing the polyfill to source consumers (it stays [Embedded]). Current source is unaffected: nothing deconstructs a KeyValuePair anymore, and on .NET the framework instance Deconstruct still wins.

The method is marked [Obsolete] with a message stating it must be removed in the next major version of Microsoft.Testing.Platform. It's scoped to the method (not the whole Polyfill type) because that partial type also hosts WaitForExitAsync, which core still uses on netstandard2.0.

The consumer-side workaround from the issue (pinning Microsoft.Testing.Extensions.Telemetry to the core version) still applies; this change is a binary-compat safety net.

Verification

  • PE-metadata probe confirms Polyfills.Polyfill.Deconstruct is emitted in the compiled net8.0 core DLL.
  • Full build.cmd -c Debug succeeds with 0 warnings / 0 errors across all TFMs (net462/472/48, net8.0, net9.0, netstandard2.0, uap).

Note

Reintroducing removed Polyfill surface is a design call — happy to drop this in favor of a packaging-only solution if maintainers prefer.

Older Microsoft.Testing extension assemblies (built before Polyfill adopted [Embedded]) reference Polyfills.Polyfill.Deconstruct in the core Microsoft.Testing.Platform assembly via InternalsVisibleTo. Dropping the Polyfill package removed that method, so pairing a newer core with an older extension (e.g. an old Telemetry) throws MissingMethodException at runtime. Re-add the single Deconstruct extension on the existing [Embedded] Polyfill partial type to restore the runtime binding without re-exposing the polyfill to source consumers.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…#9710)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 8, 2026 14:36
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 8, 2026 14:36

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 restores Polyfills.Polyfill.Deconstruct(KeyValuePair<,>, out, out) in the embedded Polyfills.Polyfill type to preserve runtime binary compatibility when newer Microsoft.Testing.Platform cores are paired with older Microsoft.Testing.* extension assemblies.

Changes:

  • Reintroduces a single Deconstruct<TKey,TValue> extension method on Polyfills.Polyfill as a binary-compatibility shim.
  • Marks the shim as [Obsolete] with guidance to remove it in the next major version.
Show a summary per file
FileDescription
src/Polyfills/Polyfill.csAdds back Polyfills.Polyfill.Deconstruct to prevent MissingMethodException with older extension binaries.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@github-actionsgithub-actionsBot 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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

✅ 22/22 dimensions clean — no findings.

Summary: Well-scoped binary-compatibility shim that restores the exact method signature old extension assemblies expect (Void Polyfills.Polyfill.Deconstruct(KeyValuePair<TKey,TValue>, TKey&, TValue&)). The fix is correct because:

  • The [Embedded] attribute on the partial declaration in ProcessExtensions.cs covers the whole type — no attribute needed on this new partial.
  • InternalsVisibleTo from Microsoft.Testing.Platform.csproj already grants access to the affected extension assemblies (Telemetry, etc.).
  • On .NET 5+, the framework's instance Deconstruct wins for source resolution; this extension is only invoked by pre-compiled old binaries via method token.
  • The [Obsolete] message is clear, actionable, and references the tracking issue.
  • No public API surface change — the class is internal.
  • The unconditional compilation (no #if guard) is intentional and correct: old extensions target both .NET and .NET Framework, so the shim must be emitted on all TFMs.

auto-merge was automatically disabled July 8, 2026 15:39

Pull request was closed

@Evangelink

Copy link
Copy Markdown
MemberAuthor

Closing as superseded by #9739, which fixes the same issue (#9710) but more completely:

  • Restores the full set of Polyfills.Polyfill members old 2.x extensions reference (Deconstruct, Contains×2, AppendJoin×2, CopyToAsync, SaveAsync), not just Deconstruct — so every old 2.x extension loads correctly on .NET Framework, not only Telemetry.
  • Guards the shim to #if !NETCOREAPP so it only lands in the netstandard2.0 asset that's actually loaded on .NET Framework.
  • Adds a reproducing acceptance test (TelemetryForwardCompatibilityTests) using the oldest supported 2.0.0 extension with telemetry enabled — verified red without the shim and green with it.

One thing worth carrying over from here: the [Obsolete] "remove in next major" annotation. I'll port that intent onto #9739.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.MissingMethodException after upgrade to 2.3.0

2 participants

@Evangelink
, '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

Restore Polyfills.Polyfill.Deconstruct for binary compatibility (#9710) - #9738

Closed
Amaury Levé (Evangelink) wants to merge 2 commits into
mainfrom
dev/amauryleve/fix-polyfill-deconstruct-compat-9710
Closed

Restore Polyfills.Polyfill.Deconstruct for binary compatibility (#9710)#9738
Amaury Levé (Evangelink) wants to merge 2 commits into
mainfrom
dev/amauryleve/fix-polyfill-deconstruct-compat-9710

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#9710.

Problem

Upgrading to MTP 2.3.0 can throw at runtime:

System.MissingMethodException: Method not found: 'Void Polyfills.Polyfill.Deconstruct(System.Collections.Generic.KeyValuePair`2<!!0,!!1>, !!0 ByRef, !!1 ByRef)'.
at Microsoft.Testing.Extensions.Telemetry.AppInsightsProvider.<IngestLoopAsync>d__18.MoveNext()

Root cause

Older Microsoft.Testing.* extension assemblies (built before the Polyfill package switched to [Embedded] types) were compiled against Polyfills.Polyfill.Deconstruct(KeyValuePair<,>, out, out) and — through InternalsVisibleTo — bound that call to the copy declared in the coreMicrosoft.Testing.Platform assembly. When Polyfill was dropped (#7726), that method disappeared, so pairing a newer core (2.3.0) with an older extension (e.g. an old Microsoft.Testing.Extensions.Telemetry pulled in transitively, as happens with xunit.v3.mtp-v2) fails with MissingMethodException.

Fix

Re-add the single Deconstruct extension onto the existing[Embedded] internal static partial class Polyfills.Polyfill that core already ships (in ProcessExtensions.cs). Core still grants InternalsVisibleTo to those extension assemblies under the same key, so this restores the runtime binding for already-shipped old extensions — without re-exposing the polyfill to source consumers (it stays [Embedded]). Current source is unaffected: nothing deconstructs a KeyValuePair anymore, and on .NET the framework instance Deconstruct still wins.

The method is marked [Obsolete] with a message stating it must be removed in the next major version of Microsoft.Testing.Platform. It's scoped to the method (not the whole Polyfill type) because that partial type also hosts WaitForExitAsync, which core still uses on netstandard2.0.

The consumer-side workaround from the issue (pinning Microsoft.Testing.Extensions.Telemetry to the core version) still applies; this change is a binary-compat safety net.

Verification

  • PE-metadata probe confirms Polyfills.Polyfill.Deconstruct is emitted in the compiled net8.0 core DLL.
  • Full build.cmd -c Debug succeeds with 0 warnings / 0 errors across all TFMs (net462/472/48, net8.0, net9.0, netstandard2.0, uap).

Note

Reintroducing removed Polyfill surface is a design call — happy to drop this in favor of a packaging-only solution if maintainers prefer.

Older Microsoft.Testing extension assemblies (built before Polyfill adopted [Embedded]) reference Polyfills.Polyfill.Deconstruct in the core Microsoft.Testing.Platform assembly via InternalsVisibleTo. Dropping the Polyfill package removed that method, so pairing a newer core with an older extension (e.g. an old Telemetry) throws MissingMethodException at runtime. Re-add the single Deconstruct extension on the existing [Embedded] Polyfill partial type to restore the runtime binding without re-exposing the polyfill to source consumers.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…#9710)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 8, 2026 14:36
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 8, 2026 14:36

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 restores Polyfills.Polyfill.Deconstruct(KeyValuePair<,>, out, out) in the embedded Polyfills.Polyfill type to preserve runtime binary compatibility when newer Microsoft.Testing.Platform cores are paired with older Microsoft.Testing.* extension assemblies.

Changes:

  • Reintroduces a single Deconstruct<TKey,TValue> extension method on Polyfills.Polyfill as a binary-compatibility shim.
  • Marks the shim as [Obsolete] with guidance to remove it in the next major version.
Show a summary per file
FileDescription
src/Polyfills/Polyfill.csAdds back Polyfills.Polyfill.Deconstruct to prevent MissingMethodException with older extension binaries.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@github-actionsgithub-actionsBot 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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

✅ 22/22 dimensions clean — no findings.

Summary: Well-scoped binary-compatibility shim that restores the exact method signature old extension assemblies expect (Void Polyfills.Polyfill.Deconstruct(KeyValuePair<TKey,TValue>, TKey&, TValue&)). The fix is correct because:

  • The [Embedded] attribute on the partial declaration in ProcessExtensions.cs covers the whole type — no attribute needed on this new partial.
  • InternalsVisibleTo from Microsoft.Testing.Platform.csproj already grants access to the affected extension assemblies (Telemetry, etc.).
  • On .NET 5+, the framework's instance Deconstruct wins for source resolution; this extension is only invoked by pre-compiled old binaries via method token.
  • The [Obsolete] message is clear, actionable, and references the tracking issue.
  • No public API surface change — the class is internal.
  • The unconditional compilation (no #if guard) is intentional and correct: old extensions target both .NET and .NET Framework, so the shim must be emitted on all TFMs.

auto-merge was automatically disabled July 8, 2026 15:39

Pull request was closed

@Evangelink

Copy link
Copy Markdown
MemberAuthor

Closing as superseded by #9739, which fixes the same issue (#9710) but more completely:

  • Restores the full set of Polyfills.Polyfill members old 2.x extensions reference (Deconstruct, Contains×2, AppendJoin×2, CopyToAsync, SaveAsync), not just Deconstruct — so every old 2.x extension loads correctly on .NET Framework, not only Telemetry.
  • Guards the shim to #if !NETCOREAPP so it only lands in the netstandard2.0 asset that's actually loaded on .NET Framework.
  • Adds a reproducing acceptance test (TelemetryForwardCompatibilityTests) using the oldest supported 2.0.0 extension with telemetry enabled — verified red without the shim and green with it.

One thing worth carrying over from here: the [Obsolete] "remove in next major" annotation. I'll port that intent onto #9739.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.MissingMethodException after upgrade to 2.3.0

2 participants

@Evangelink
, '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

Restore Polyfills.Polyfill.Deconstruct for binary compatibility (#9710) - #9738

Closed
Amaury Levé (Evangelink) wants to merge 2 commits into
mainfrom
dev/amauryleve/fix-polyfill-deconstruct-compat-9710
Closed

Restore Polyfills.Polyfill.Deconstruct for binary compatibility (#9710)#9738
Amaury Levé (Evangelink) wants to merge 2 commits into
mainfrom
dev/amauryleve/fix-polyfill-deconstruct-compat-9710

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#9710.

Problem

Upgrading to MTP 2.3.0 can throw at runtime:

System.MissingMethodException: Method not found: 'Void Polyfills.Polyfill.Deconstruct(System.Collections.Generic.KeyValuePair`2<!!0,!!1>, !!0 ByRef, !!1 ByRef)'.
at Microsoft.Testing.Extensions.Telemetry.AppInsightsProvider.<IngestLoopAsync>d__18.MoveNext()

Root cause

Older Microsoft.Testing.* extension assemblies (built before the Polyfill package switched to [Embedded] types) were compiled against Polyfills.Polyfill.Deconstruct(KeyValuePair<,>, out, out) and — through InternalsVisibleTo — bound that call to the copy declared in the coreMicrosoft.Testing.Platform assembly. When Polyfill was dropped (#7726), that method disappeared, so pairing a newer core (2.3.0) with an older extension (e.g. an old Microsoft.Testing.Extensions.Telemetry pulled in transitively, as happens with xunit.v3.mtp-v2) fails with MissingMethodException.

Fix

Re-add the single Deconstruct extension onto the existing[Embedded] internal static partial class Polyfills.Polyfill that core already ships (in ProcessExtensions.cs). Core still grants InternalsVisibleTo to those extension assemblies under the same key, so this restores the runtime binding for already-shipped old extensions — without re-exposing the polyfill to source consumers (it stays [Embedded]). Current source is unaffected: nothing deconstructs a KeyValuePair anymore, and on .NET the framework instance Deconstruct still wins.

The method is marked [Obsolete] with a message stating it must be removed in the next major version of Microsoft.Testing.Platform. It's scoped to the method (not the whole Polyfill type) because that partial type also hosts WaitForExitAsync, which core still uses on netstandard2.0.

The consumer-side workaround from the issue (pinning Microsoft.Testing.Extensions.Telemetry to the core version) still applies; this change is a binary-compat safety net.

Verification

  • PE-metadata probe confirms Polyfills.Polyfill.Deconstruct is emitted in the compiled net8.0 core DLL.
  • Full build.cmd -c Debug succeeds with 0 warnings / 0 errors across all TFMs (net462/472/48, net8.0, net9.0, netstandard2.0, uap).

Note

Reintroducing removed Polyfill surface is a design call — happy to drop this in favor of a packaging-only solution if maintainers prefer.

Older Microsoft.Testing extension assemblies (built before Polyfill adopted [Embedded]) reference Polyfills.Polyfill.Deconstruct in the core Microsoft.Testing.Platform assembly via InternalsVisibleTo. Dropping the Polyfill package removed that method, so pairing a newer core with an older extension (e.g. an old Telemetry) throws MissingMethodException at runtime. Re-add the single Deconstruct extension on the existing [Embedded] Polyfill partial type to restore the runtime binding without re-exposing the polyfill to source consumers.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…#9710)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 8, 2026 14:36
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 8, 2026 14:36

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 restores Polyfills.Polyfill.Deconstruct(KeyValuePair<,>, out, out) in the embedded Polyfills.Polyfill type to preserve runtime binary compatibility when newer Microsoft.Testing.Platform cores are paired with older Microsoft.Testing.* extension assemblies.

Changes:

  • Reintroduces a single Deconstruct<TKey,TValue> extension method on Polyfills.Polyfill as a binary-compatibility shim.
  • Marks the shim as [Obsolete] with guidance to remove it in the next major version.
Show a summary per file
FileDescription
src/Polyfills/Polyfill.csAdds back Polyfills.Polyfill.Deconstruct to prevent MissingMethodException with older extension binaries.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@github-actionsgithub-actionsBot 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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

✅ 22/22 dimensions clean — no findings.

Summary: Well-scoped binary-compatibility shim that restores the exact method signature old extension assemblies expect (Void Polyfills.Polyfill.Deconstruct(KeyValuePair<TKey,TValue>, TKey&, TValue&)). The fix is correct because:

  • The [Embedded] attribute on the partial declaration in ProcessExtensions.cs covers the whole type — no attribute needed on this new partial.
  • InternalsVisibleTo from Microsoft.Testing.Platform.csproj already grants access to the affected extension assemblies (Telemetry, etc.).
  • On .NET 5+, the framework's instance Deconstruct wins for source resolution; this extension is only invoked by pre-compiled old binaries via method token.
  • The [Obsolete] message is clear, actionable, and references the tracking issue.
  • No public API surface change — the class is internal.
  • The unconditional compilation (no #if guard) is intentional and correct: old extensions target both .NET and .NET Framework, so the shim must be emitted on all TFMs.

auto-merge was automatically disabled July 8, 2026 15:39

Pull request was closed

@Evangelink

Copy link
Copy Markdown
MemberAuthor

Closing as superseded by #9739, which fixes the same issue (#9710) but more completely:

  • Restores the full set of Polyfills.Polyfill members old 2.x extensions reference (Deconstruct, Contains×2, AppendJoin×2, CopyToAsync, SaveAsync), not just Deconstruct — so every old 2.x extension loads correctly on .NET Framework, not only Telemetry.
  • Guards the shim to #if !NETCOREAPP so it only lands in the netstandard2.0 asset that's actually loaded on .NET Framework.
  • Adds a reproducing acceptance test (TelemetryForwardCompatibilityTests) using the oldest supported 2.0.0 extension with telemetry enabled — verified red without the shim and green with it.

One thing worth carrying over from here: the [Obsolete] "remove in next major" annotation. I'll port that intent onto #9739.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.MissingMethodException after upgrade to 2.3.0

2 participants

@Evangelink
, '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

Restore Polyfills.Polyfill.Deconstruct for binary compatibility (#9710) - #9738

Closed
Amaury Levé (Evangelink) wants to merge 2 commits into
mainfrom
dev/amauryleve/fix-polyfill-deconstruct-compat-9710
Closed

Restore Polyfills.Polyfill.Deconstruct for binary compatibility (#9710)#9738
Amaury Levé (Evangelink) wants to merge 2 commits into
mainfrom
dev/amauryleve/fix-polyfill-deconstruct-compat-9710

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#9710.

Problem

Upgrading to MTP 2.3.0 can throw at runtime:

System.MissingMethodException: Method not found: 'Void Polyfills.Polyfill.Deconstruct(System.Collections.Generic.KeyValuePair`2<!!0,!!1>, !!0 ByRef, !!1 ByRef)'.
at Microsoft.Testing.Extensions.Telemetry.AppInsightsProvider.<IngestLoopAsync>d__18.MoveNext()

Root cause

Older Microsoft.Testing.* extension assemblies (built before the Polyfill package switched to [Embedded] types) were compiled against Polyfills.Polyfill.Deconstruct(KeyValuePair<,>, out, out) and — through InternalsVisibleTo — bound that call to the copy declared in the coreMicrosoft.Testing.Platform assembly. When Polyfill was dropped (#7726), that method disappeared, so pairing a newer core (2.3.0) with an older extension (e.g. an old Microsoft.Testing.Extensions.Telemetry pulled in transitively, as happens with xunit.v3.mtp-v2) fails with MissingMethodException.

Fix

Re-add the single Deconstruct extension onto the existing[Embedded] internal static partial class Polyfills.Polyfill that core already ships (in ProcessExtensions.cs). Core still grants InternalsVisibleTo to those extension assemblies under the same key, so this restores the runtime binding for already-shipped old extensions — without re-exposing the polyfill to source consumers (it stays [Embedded]). Current source is unaffected: nothing deconstructs a KeyValuePair anymore, and on .NET the framework instance Deconstruct still wins.

The method is marked [Obsolete] with a message stating it must be removed in the next major version of Microsoft.Testing.Platform. It's scoped to the method (not the whole Polyfill type) because that partial type also hosts WaitForExitAsync, which core still uses on netstandard2.0.

The consumer-side workaround from the issue (pinning Microsoft.Testing.Extensions.Telemetry to the core version) still applies; this change is a binary-compat safety net.

Verification

  • PE-metadata probe confirms Polyfills.Polyfill.Deconstruct is emitted in the compiled net8.0 core DLL.
  • Full build.cmd -c Debug succeeds with 0 warnings / 0 errors across all TFMs (net462/472/48, net8.0, net9.0, netstandard2.0, uap).

Note

Reintroducing removed Polyfill surface is a design call — happy to drop this in favor of a packaging-only solution if maintainers prefer.

Older Microsoft.Testing extension assemblies (built before Polyfill adopted [Embedded]) reference Polyfills.Polyfill.Deconstruct in the core Microsoft.Testing.Platform assembly via InternalsVisibleTo. Dropping the Polyfill package removed that method, so pairing a newer core with an older extension (e.g. an old Telemetry) throws MissingMethodException at runtime. Re-add the single Deconstruct extension on the existing [Embedded] Polyfill partial type to restore the runtime binding without re-exposing the polyfill to source consumers.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…#9710)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 8, 2026 14:36
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 8, 2026 14:36

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 restores Polyfills.Polyfill.Deconstruct(KeyValuePair<,>, out, out) in the embedded Polyfills.Polyfill type to preserve runtime binary compatibility when newer Microsoft.Testing.Platform cores are paired with older Microsoft.Testing.* extension assemblies.

Changes:

  • Reintroduces a single Deconstruct<TKey,TValue> extension method on Polyfills.Polyfill as a binary-compatibility shim.
  • Marks the shim as [Obsolete] with guidance to remove it in the next major version.
Show a summary per file
FileDescription
src/Polyfills/Polyfill.csAdds back Polyfills.Polyfill.Deconstruct to prevent MissingMethodException with older extension binaries.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@github-actionsgithub-actionsBot 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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

✅ 22/22 dimensions clean — no findings.

Summary: Well-scoped binary-compatibility shim that restores the exact method signature old extension assemblies expect (Void Polyfills.Polyfill.Deconstruct(KeyValuePair<TKey,TValue>, TKey&, TValue&)). The fix is correct because:

  • The [Embedded] attribute on the partial declaration in ProcessExtensions.cs covers the whole type — no attribute needed on this new partial.
  • InternalsVisibleTo from Microsoft.Testing.Platform.csproj already grants access to the affected extension assemblies (Telemetry, etc.).
  • On .NET 5+, the framework's instance Deconstruct wins for source resolution; this extension is only invoked by pre-compiled old binaries via method token.
  • The [Obsolete] message is clear, actionable, and references the tracking issue.
  • No public API surface change — the class is internal.
  • The unconditional compilation (no #if guard) is intentional and correct: old extensions target both .NET and .NET Framework, so the shim must be emitted on all TFMs.

auto-merge was automatically disabled July 8, 2026 15:39

Pull request was closed

@Evangelink

Copy link
Copy Markdown
MemberAuthor

Closing as superseded by #9739, which fixes the same issue (#9710) but more completely:

  • Restores the full set of Polyfills.Polyfill members old 2.x extensions reference (Deconstruct, Contains×2, AppendJoin×2, CopyToAsync, SaveAsync), not just Deconstruct — so every old 2.x extension loads correctly on .NET Framework, not only Telemetry.
  • Guards the shim to #if !NETCOREAPP so it only lands in the netstandard2.0 asset that's actually loaded on .NET Framework.
  • Adds a reproducing acceptance test (TelemetryForwardCompatibilityTests) using the oldest supported 2.0.0 extension with telemetry enabled — verified red without the shim and green with it.

One thing worth carrying over from here: the [Obsolete] "remove in next major" annotation. I'll port that intent onto #9739.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.MissingMethodException after upgrade to 2.3.0

2 participants

@Evangelink
, '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

Restore Polyfills.Polyfill.Deconstruct for binary compatibility (#9710) - #9738

Closed
Amaury Levé (Evangelink) wants to merge 2 commits into
mainfrom
dev/amauryleve/fix-polyfill-deconstruct-compat-9710
Closed

Restore Polyfills.Polyfill.Deconstruct for binary compatibility (#9710)#9738
Amaury Levé (Evangelink) wants to merge 2 commits into
mainfrom
dev/amauryleve/fix-polyfill-deconstruct-compat-9710

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#9710.

Problem

Upgrading to MTP 2.3.0 can throw at runtime:

System.MissingMethodException: Method not found: 'Void Polyfills.Polyfill.Deconstruct(System.Collections.Generic.KeyValuePair`2<!!0,!!1>, !!0 ByRef, !!1 ByRef)'.
at Microsoft.Testing.Extensions.Telemetry.AppInsightsProvider.<IngestLoopAsync>d__18.MoveNext()

Root cause

Older Microsoft.Testing.* extension assemblies (built before the Polyfill package switched to [Embedded] types) were compiled against Polyfills.Polyfill.Deconstruct(KeyValuePair<,>, out, out) and — through InternalsVisibleTo — bound that call to the copy declared in the coreMicrosoft.Testing.Platform assembly. When Polyfill was dropped (#7726), that method disappeared, so pairing a newer core (2.3.0) with an older extension (e.g. an old Microsoft.Testing.Extensions.Telemetry pulled in transitively, as happens with xunit.v3.mtp-v2) fails with MissingMethodException.

Fix

Re-add the single Deconstruct extension onto the existing[Embedded] internal static partial class Polyfills.Polyfill that core already ships (in ProcessExtensions.cs). Core still grants InternalsVisibleTo to those extension assemblies under the same key, so this restores the runtime binding for already-shipped old extensions — without re-exposing the polyfill to source consumers (it stays [Embedded]). Current source is unaffected: nothing deconstructs a KeyValuePair anymore, and on .NET the framework instance Deconstruct still wins.

The method is marked [Obsolete] with a message stating it must be removed in the next major version of Microsoft.Testing.Platform. It's scoped to the method (not the whole Polyfill type) because that partial type also hosts WaitForExitAsync, which core still uses on netstandard2.0.

The consumer-side workaround from the issue (pinning Microsoft.Testing.Extensions.Telemetry to the core version) still applies; this change is a binary-compat safety net.

Verification

  • PE-metadata probe confirms Polyfills.Polyfill.Deconstruct is emitted in the compiled net8.0 core DLL.
  • Full build.cmd -c Debug succeeds with 0 warnings / 0 errors across all TFMs (net462/472/48, net8.0, net9.0, netstandard2.0, uap).

Note

Reintroducing removed Polyfill surface is a design call — happy to drop this in favor of a packaging-only solution if maintainers prefer.

Older Microsoft.Testing extension assemblies (built before Polyfill adopted [Embedded]) reference Polyfills.Polyfill.Deconstruct in the core Microsoft.Testing.Platform assembly via InternalsVisibleTo. Dropping the Polyfill package removed that method, so pairing a newer core with an older extension (e.g. an old Telemetry) throws MissingMethodException at runtime. Re-add the single Deconstruct extension on the existing [Embedded] Polyfill partial type to restore the runtime binding without re-exposing the polyfill to source consumers.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…#9710)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 8, 2026 14:36
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 8, 2026 14:36

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 restores Polyfills.Polyfill.Deconstruct(KeyValuePair<,>, out, out) in the embedded Polyfills.Polyfill type to preserve runtime binary compatibility when newer Microsoft.Testing.Platform cores are paired with older Microsoft.Testing.* extension assemblies.

Changes:

  • Reintroduces a single Deconstruct<TKey,TValue> extension method on Polyfills.Polyfill as a binary-compatibility shim.
  • Marks the shim as [Obsolete] with guidance to remove it in the next major version.
Show a summary per file
FileDescription
src/Polyfills/Polyfill.csAdds back Polyfills.Polyfill.Deconstruct to prevent MissingMethodException with older extension binaries.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@github-actionsgithub-actionsBot 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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

✅ 22/22 dimensions clean — no findings.

Summary: Well-scoped binary-compatibility shim that restores the exact method signature old extension assemblies expect (Void Polyfills.Polyfill.Deconstruct(KeyValuePair<TKey,TValue>, TKey&, TValue&)). The fix is correct because:

  • The [Embedded] attribute on the partial declaration in ProcessExtensions.cs covers the whole type — no attribute needed on this new partial.
  • InternalsVisibleTo from Microsoft.Testing.Platform.csproj already grants access to the affected extension assemblies (Telemetry, etc.).
  • On .NET 5+, the framework's instance Deconstruct wins for source resolution; this extension is only invoked by pre-compiled old binaries via method token.
  • The [Obsolete] message is clear, actionable, and references the tracking issue.
  • No public API surface change — the class is internal.
  • The unconditional compilation (no #if guard) is intentional and correct: old extensions target both .NET and .NET Framework, so the shim must be emitted on all TFMs.

auto-merge was automatically disabled July 8, 2026 15:39

Pull request was closed

@Evangelink

Copy link
Copy Markdown
MemberAuthor

Closing as superseded by #9739, which fixes the same issue (#9710) but more completely:

  • Restores the full set of Polyfills.Polyfill members old 2.x extensions reference (Deconstruct, Contains×2, AppendJoin×2, CopyToAsync, SaveAsync), not just Deconstruct — so every old 2.x extension loads correctly on .NET Framework, not only Telemetry.
  • Guards the shim to #if !NETCOREAPP so it only lands in the netstandard2.0 asset that's actually loaded on .NET Framework.
  • Adds a reproducing acceptance test (TelemetryForwardCompatibilityTests) using the oldest supported 2.0.0 extension with telemetry enabled — verified red without the shim and green with it.

One thing worth carrying over from here: the [Obsolete] "remove in next major" annotation. I'll port that intent onto #9739.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.MissingMethodException after upgrade to 2.3.0

2 participants

@Evangelink
, '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

Restore Polyfills.Polyfill.Deconstruct for binary compatibility (#9710) - #9738

Closed
Amaury Levé (Evangelink) wants to merge 2 commits into
mainfrom
dev/amauryleve/fix-polyfill-deconstruct-compat-9710
Closed

Restore Polyfills.Polyfill.Deconstruct for binary compatibility (#9710)#9738
Amaury Levé (Evangelink) wants to merge 2 commits into
mainfrom
dev/amauryleve/fix-polyfill-deconstruct-compat-9710

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#9710.

Problem

Upgrading to MTP 2.3.0 can throw at runtime:

System.MissingMethodException: Method not found: 'Void Polyfills.Polyfill.Deconstruct(System.Collections.Generic.KeyValuePair`2<!!0,!!1>, !!0 ByRef, !!1 ByRef)'.
at Microsoft.Testing.Extensions.Telemetry.AppInsightsProvider.<IngestLoopAsync>d__18.MoveNext()

Root cause

Older Microsoft.Testing.* extension assemblies (built before the Polyfill package switched to [Embedded] types) were compiled against Polyfills.Polyfill.Deconstruct(KeyValuePair<,>, out, out) and — through InternalsVisibleTo — bound that call to the copy declared in the coreMicrosoft.Testing.Platform assembly. When Polyfill was dropped (#7726), that method disappeared, so pairing a newer core (2.3.0) with an older extension (e.g. an old Microsoft.Testing.Extensions.Telemetry pulled in transitively, as happens with xunit.v3.mtp-v2) fails with MissingMethodException.

Fix

Re-add the single Deconstruct extension onto the existing[Embedded] internal static partial class Polyfills.Polyfill that core already ships (in ProcessExtensions.cs). Core still grants InternalsVisibleTo to those extension assemblies under the same key, so this restores the runtime binding for already-shipped old extensions — without re-exposing the polyfill to source consumers (it stays [Embedded]). Current source is unaffected: nothing deconstructs a KeyValuePair anymore, and on .NET the framework instance Deconstruct still wins.

The method is marked [Obsolete] with a message stating it must be removed in the next major version of Microsoft.Testing.Platform. It's scoped to the method (not the whole Polyfill type) because that partial type also hosts WaitForExitAsync, which core still uses on netstandard2.0.

The consumer-side workaround from the issue (pinning Microsoft.Testing.Extensions.Telemetry to the core version) still applies; this change is a binary-compat safety net.

Verification

  • PE-metadata probe confirms Polyfills.Polyfill.Deconstruct is emitted in the compiled net8.0 core DLL.
  • Full build.cmd -c Debug succeeds with 0 warnings / 0 errors across all TFMs (net462/472/48, net8.0, net9.0, netstandard2.0, uap).

Note

Reintroducing removed Polyfill surface is a design call — happy to drop this in favor of a packaging-only solution if maintainers prefer.

Older Microsoft.Testing extension assemblies (built before Polyfill adopted [Embedded]) reference Polyfills.Polyfill.Deconstruct in the core Microsoft.Testing.Platform assembly via InternalsVisibleTo. Dropping the Polyfill package removed that method, so pairing a newer core with an older extension (e.g. an old Telemetry) throws MissingMethodException at runtime. Re-add the single Deconstruct extension on the existing [Embedded] Polyfill partial type to restore the runtime binding without re-exposing the polyfill to source consumers.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…#9710)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 8, 2026 14:36
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 8, 2026 14:36

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 restores Polyfills.Polyfill.Deconstruct(KeyValuePair<,>, out, out) in the embedded Polyfills.Polyfill type to preserve runtime binary compatibility when newer Microsoft.Testing.Platform cores are paired with older Microsoft.Testing.* extension assemblies.

Changes:

  • Reintroduces a single Deconstruct<TKey,TValue> extension method on Polyfills.Polyfill as a binary-compatibility shim.
  • Marks the shim as [Obsolete] with guidance to remove it in the next major version.
Show a summary per file
FileDescription
src/Polyfills/Polyfill.csAdds back Polyfills.Polyfill.Deconstruct to prevent MissingMethodException with older extension binaries.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@github-actionsgithub-actionsBot 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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

✅ 22/22 dimensions clean — no findings.

Summary: Well-scoped binary-compatibility shim that restores the exact method signature old extension assemblies expect (Void Polyfills.Polyfill.Deconstruct(KeyValuePair<TKey,TValue>, TKey&, TValue&)). The fix is correct because:

  • The [Embedded] attribute on the partial declaration in ProcessExtensions.cs covers the whole type — no attribute needed on this new partial.
  • InternalsVisibleTo from Microsoft.Testing.Platform.csproj already grants access to the affected extension assemblies (Telemetry, etc.).
  • On .NET 5+, the framework's instance Deconstruct wins for source resolution; this extension is only invoked by pre-compiled old binaries via method token.
  • The [Obsolete] message is clear, actionable, and references the tracking issue.
  • No public API surface change — the class is internal.
  • The unconditional compilation (no #if guard) is intentional and correct: old extensions target both .NET and .NET Framework, so the shim must be emitted on all TFMs.

auto-merge was automatically disabled July 8, 2026 15:39

Pull request was closed

@Evangelink

Copy link
Copy Markdown
MemberAuthor

Closing as superseded by #9739, which fixes the same issue (#9710) but more completely:

  • Restores the full set of Polyfills.Polyfill members old 2.x extensions reference (Deconstruct, Contains×2, AppendJoin×2, CopyToAsync, SaveAsync), not just Deconstruct — so every old 2.x extension loads correctly on .NET Framework, not only Telemetry.
  • Guards the shim to #if !NETCOREAPP so it only lands in the netstandard2.0 asset that's actually loaded on .NET Framework.
  • Adds a reproducing acceptance test (TelemetryForwardCompatibilityTests) using the oldest supported 2.0.0 extension with telemetry enabled — verified red without the shim and green with it.

One thing worth carrying over from here: the [Obsolete] "remove in next major" annotation. I'll port that intent onto #9739.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.MissingMethodException after upgrade to 2.3.0

2 participants

@Evangelink
, '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

Restore Polyfills.Polyfill.Deconstruct for binary compatibility (#9710) - #9738

Closed
Amaury Levé (Evangelink) wants to merge 2 commits into
mainfrom
dev/amauryleve/fix-polyfill-deconstruct-compat-9710
Closed

Restore Polyfills.Polyfill.Deconstruct for binary compatibility (#9710)#9738
Amaury Levé (Evangelink) wants to merge 2 commits into
mainfrom
dev/amauryleve/fix-polyfill-deconstruct-compat-9710

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#9710.

Problem

Upgrading to MTP 2.3.0 can throw at runtime:

System.MissingMethodException: Method not found: 'Void Polyfills.Polyfill.Deconstruct(System.Collections.Generic.KeyValuePair`2<!!0,!!1>, !!0 ByRef, !!1 ByRef)'.
at Microsoft.Testing.Extensions.Telemetry.AppInsightsProvider.<IngestLoopAsync>d__18.MoveNext()

Root cause

Older Microsoft.Testing.* extension assemblies (built before the Polyfill package switched to [Embedded] types) were compiled against Polyfills.Polyfill.Deconstruct(KeyValuePair<,>, out, out) and — through InternalsVisibleTo — bound that call to the copy declared in the coreMicrosoft.Testing.Platform assembly. When Polyfill was dropped (#7726), that method disappeared, so pairing a newer core (2.3.0) with an older extension (e.g. an old Microsoft.Testing.Extensions.Telemetry pulled in transitively, as happens with xunit.v3.mtp-v2) fails with MissingMethodException.

Fix

Re-add the single Deconstruct extension onto the existing[Embedded] internal static partial class Polyfills.Polyfill that core already ships (in ProcessExtensions.cs). Core still grants InternalsVisibleTo to those extension assemblies under the same key, so this restores the runtime binding for already-shipped old extensions — without re-exposing the polyfill to source consumers (it stays [Embedded]). Current source is unaffected: nothing deconstructs a KeyValuePair anymore, and on .NET the framework instance Deconstruct still wins.

The method is marked [Obsolete] with a message stating it must be removed in the next major version of Microsoft.Testing.Platform. It's scoped to the method (not the whole Polyfill type) because that partial type also hosts WaitForExitAsync, which core still uses on netstandard2.0.

The consumer-side workaround from the issue (pinning Microsoft.Testing.Extensions.Telemetry to the core version) still applies; this change is a binary-compat safety net.

Verification

  • PE-metadata probe confirms Polyfills.Polyfill.Deconstruct is emitted in the compiled net8.0 core DLL.
  • Full build.cmd -c Debug succeeds with 0 warnings / 0 errors across all TFMs (net462/472/48, net8.0, net9.0, netstandard2.0, uap).

Note

Reintroducing removed Polyfill surface is a design call — happy to drop this in favor of a packaging-only solution if maintainers prefer.

Older Microsoft.Testing extension assemblies (built before Polyfill adopted [Embedded]) reference Polyfills.Polyfill.Deconstruct in the core Microsoft.Testing.Platform assembly via InternalsVisibleTo. Dropping the Polyfill package removed that method, so pairing a newer core with an older extension (e.g. an old Telemetry) throws MissingMethodException at runtime. Re-add the single Deconstruct extension on the existing [Embedded] Polyfill partial type to restore the runtime binding without re-exposing the polyfill to source consumers.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…#9710)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 8, 2026 14:36
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 8, 2026 14:36

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 restores Polyfills.Polyfill.Deconstruct(KeyValuePair<,>, out, out) in the embedded Polyfills.Polyfill type to preserve runtime binary compatibility when newer Microsoft.Testing.Platform cores are paired with older Microsoft.Testing.* extension assemblies.

Changes:

  • Reintroduces a single Deconstruct<TKey,TValue> extension method on Polyfills.Polyfill as a binary-compatibility shim.
  • Marks the shim as [Obsolete] with guidance to remove it in the next major version.
Show a summary per file
FileDescription
src/Polyfills/Polyfill.csAdds back Polyfills.Polyfill.Deconstruct to prevent MissingMethodException with older extension binaries.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Low

@github-actionsgithub-actionsBot 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.

Note

🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

✅ 22/22 dimensions clean — no findings.

Summary: Well-scoped binary-compatibility shim that restores the exact method signature old extension assemblies expect (Void Polyfills.Polyfill.Deconstruct(KeyValuePair<TKey,TValue>, TKey&, TValue&)). The fix is correct because:

  • The [Embedded] attribute on the partial declaration in ProcessExtensions.cs covers the whole type — no attribute needed on this new partial.
  • InternalsVisibleTo from Microsoft.Testing.Platform.csproj already grants access to the affected extension assemblies (Telemetry, etc.).
  • On .NET 5+, the framework's instance Deconstruct wins for source resolution; this extension is only invoked by pre-compiled old binaries via method token.
  • The [Obsolete] message is clear, actionable, and references the tracking issue.
  • No public API surface change — the class is internal.
  • The unconditional compilation (no #if guard) is intentional and correct: old extensions target both .NET and .NET Framework, so the shim must be emitted on all TFMs.

auto-merge was automatically disabled July 8, 2026 15:39

Pull request was closed

@Evangelink

Copy link
Copy Markdown
MemberAuthor

Closing as superseded by #9739, which fixes the same issue (#9710) but more completely:

  • Restores the full set of Polyfills.Polyfill members old 2.x extensions reference (Deconstruct, Contains×2, AppendJoin×2, CopyToAsync, SaveAsync), not just Deconstruct — so every old 2.x extension loads correctly on .NET Framework, not only Telemetry.
  • Guards the shim to #if !NETCOREAPP so it only lands in the netstandard2.0 asset that's actually loaded on .NET Framework.
  • Adds a reproducing acceptance test (TelemetryForwardCompatibilityTests) using the oldest supported 2.0.0 extension with telemetry enabled — verified red without the shim and green with it.

One thing worth carrying over from here: the [Obsolete] "remove in next major" annotation. I'll port that intent onto #9739.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System.MissingMethodException after upgrade to 2.3.0

2 participants

@Evangelink