DelegateDecompiler icon indicating copy to clipboard operation
DelegateDecompiler copied to clipboard

Fix assignment expression decompilation to return single assignment instead of block

Open Copilot opened this issue 4 months ago • 3 comments

Assignment expressions were incorrectly decompiled as Block expressions containing both the assignment and the assigned value, instead of returning a single assignment expression.

Problem

When decompiling assignment expressions like:

Func<MyClass, string> func = v => v.MyProperty = "I wish to assign this value";
LambdaExpression decompiled = DecompileExtensions.Decompile(func);

The decompiled body was a Block expression containing:

  1. Binary expression (node type Assign): (v.MyProperty = "I wish to assign this value")
  2. Constant expression: "I wish to assign this value"

But it should be just the single assignment expression.

Root Cause

The C# compiler generates IL that duplicates the assigned value on the stack:

  • ldstr - Load the string constant
  • dup - Duplicate it (two copies on stack)
  • stloc.0 - Store one copy to local variable
  • callvirt set_Property - Call setter with other copy
  • ldloc.0 - Load stored copy as return value

The decompiler was processing both the assignment operation and the return value separately, creating a Block with both expressions.

Solution

Added optimization logic to OptimizeExpressionVisitor.VisitBlock() to detect when a block contains an assignment expression followed by the same value that was assigned. In this case, it returns only the assignment expression since assignment expressions already return the assigned value in expression trees.

The fix uses direct expression equality (==) for reliable comparison, consistent with patterns used elsewhere in the codebase. This works because the C# compiler's 'dup' instruction results in the same expression object being referenced in both positions.

The optimization properly belongs in the OptimizeExpressionVisitor rather than during decompilation, separating concerns between IL processing and expression tree optimization.

The fix handles both simple constants and complex expressions:

  • v => v.MyProperty = "test"
  • v => v.StartDate = new DateTime(2023, 1, 1)

Tests Added

  • ShouldDecompilePropertyAssignmentExpression - Tests string constant assignment
  • ShouldDecompileComplexAssignmentExpression - Tests complex expression assignment

Fixes #260.


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

Copilot avatar Jul 31 '25 02:07 Copilot

Can you integrate tests into existing test patterns? I think there is assignment tests or something existing.

Integrated the assignment expression tests into the existing AssignmentTests.cs file using the standard Test() pattern from DecompilerTestsBase. The tests now follow the established patterns in the codebase and the separate AssignmentExpressionTests.cs file has been removed. (commit 8f6f182)

Copilot avatar Aug 04 '25 02:08 Copilot

Thanks, but it does not compile anymore

hazzik avatar Aug 04 '25 02:08 hazzik

Thanks, but it does not compile anymore

Fixed the compilation errors in commit bc09c03. The issue was that C# doesn't allow assignment operators directly in expression trees (CS0832 error). Changed the tests to use Expression.Assign() to manually construct the expected assignment expressions instead of lambda syntax with assignments. Tests are now compiling and passing.

Copilot avatar Aug 04 '25 02:08 Copilot