Skip to content

Fix ObjectIsFrozenException when using string.IsNullOrEmpty/IsNullOrWhiteSpace in Where lambda bindings#2014

Draft
Copilot wants to merge 3 commits intomainfrom
copilot/fix-binding-translation-bug
Draft

Fix ObjectIsFrozenException when using string.IsNullOrEmpty/IsNullOrWhiteSpace in Where lambda bindings#2014
Copilot wants to merge 3 commits intomainfrom
copilot/fix-binding-translation-bug

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 2, 2026

Bindings like {value: _root.SessionsToShow.Where(s => string.IsNullOrEmpty(s.CategoryId)).ToList()} threw ObjectIsFrozenException when computing SimplePathExpressionBindingProperty.

Root cause

string.IsNullOrEmpty translates to !(a?.length > 0) — a JsBinaryExpression (comparison, precedence 12) inside a JsUnaryExpression (!, precedence 16). Because 12 < 16, JsParensFixingVisitor tries to wrap the binary expression in a JsParenthesizedExpression via ReplaceWith. However, FormatParametrizedScript was called directly on the frozen KnockoutJsExpressionBindingProperty expression (frozen after compilation), so ReplaceWith hit ThrowIfFrozen().

FormatJavascript already handles this correctly by always cloning before transformation; FormatSimplePath did not.

Fix

  • JsAstHelpers.FormatParametrizedScript: clone the node if frozen before calling FixParenthesis, which mutates the tree in-place.
// FixParenthesis modifies the tree in-place; if the node is frozen we must clone it first
if (node.IsFrozen) node = node.Clone();
node = node.FixParenthesis();
  • JavascriptCompilationTests: added regression tests verifying SimplePathExpressionBindingProperty is computed without exception for Where(v => string.IsNullOrEmpty(...)) and Where(v => string.IsNullOrWhiteSpace(...)).

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • 0t3vsblobprodcus362.vsblob.vsassets.io
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/dotvvm/dotvvm/src/DotVVM.sln --packages /tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true (dns block)
  • 1p7vsblobprodcus324.vsblob.vsassets.io
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/dotvvm/dotvvm/src/DotVVM.sln --packages /tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true (dns block)
  • 4m6vsblobprodcus384.vsblob.vsassets.io
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/dotvvm/dotvvm/src/DotVVM.sln --packages /tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true (dns block)
  • 7devsblobprodcus323.vsblob.vsassets.io
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/00DA3BA51762B14D47D4C9F6E8A232CB/missingpackages_workingdir --packages /tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/missingpackages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --configfile /tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/nugetconfig/nuget.config --force (dns block)
  • cflvsblobprodcus383.vsblob.vsassets.io
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/dotvvm/dotvvm/src/DotVVM.sln --packages /tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true (dns block)
  • jd4vsblobprodcus366.vsblob.vsassets.io
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/dotvvm/dotvvm/src/DotVVM.sln --packages /tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true (dns block)
  • k0ivsblobprodcus356.vsblob.vsassets.io
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/dotvvm/dotvvm/src/DotVVM.sln --packages /tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true (dns block)
  • k4kvsblobprodcus344.vsblob.vsassets.io
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/dotvvm/dotvvm/src/DotVVM.sln --packages /tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true (dns block)
  • m6xvsblobprodcus342.vsblob.vsassets.io
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/dotvvm/dotvvm/src/DotVVM.sln --packages /tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true (dns block)
  • pdfvsblobprodcus380.vsblob.vsassets.io
    • Triggering command: /usr/bin/dotnet dotnet build src/Tests/DotVVM.Framework.Tests.csproj -c Release (dns block)
  • pe3vsblobprodcus354.vsblob.vsassets.io
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/dotvvm/dotvvm/src/DotVVM.sln --packages /tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true (dns block)
  • yttvsblobprodcus357.vsblob.vsassets.io
    • Triggering command: /usr/bin/dotnet dotnet restore --no-dependencies /home/REDACTED/work/dotvvm/dotvvm/src/DotVVM.sln --packages /tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/tmp/codeql-scratch-bfcb369f5dc91785/dbs/csharp/working/emptyFakeDotnetRoot /p:AllowMissingPrunePackageData=true (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI linked an issue Apr 2, 2026 that may be closed by this pull request
…ranslation

Agent-Logs-Url: https://github.com/riganti/dotvvm/sessions/13a597db-6d37-4df2-99f1-73f83128f1c8

Co-authored-by: tomasherceg <5599524+tomasherceg@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix bug in binding translation for expressions Fix ObjectIsFrozenException when using string.IsNullOrEmpty/IsNullOrWhiteSpace in Where lambda bindings Apr 2, 2026
Copilot AI requested a review from tomasherceg April 2, 2026 10:08
var binding = bindingHelper.ValueBinding<IEnumerable<TestViewModel2>>(
expression,
new[] { typeof(TestViewModel) });
var simplePath = binding.GetProperty(typeof(SimplePathExpressionBindingProperty), ErrorHandlingMode.ThrowException);
Sign up for free to 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.

Bug in binding translation

2 participants