Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
5 changes: 5 additions & 0 deletions .gitignore.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Temporary modifications for development/testing
# These files have been temporarily modified to disable GitVersion and signing for testing
src/Directory.Build.props
src/DelegateDecompiler/AssemblyInfo.cs
global.json
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "9.0.100",
"version": "8.0.118",
"rollForward": "latestFeature"
}
}
53 changes: 53 additions & 0 deletions src/DelegateDecompiler.Tests/AssignmentExpressionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Linq.Expressions;
using NUnit.Framework;

namespace DelegateDecompiler.Tests
{
[TestFixture]
public class AssignmentExpressionTests : DecompilerTestsBase
{
class MyClass
{
public string MyProperty { get; set; } = "";
}

[Test]
public void ShouldDecompilePropertyAssignmentAsAssignmentExpression()
{
// Create a function that assigns to a property
string TestAssignment(MyClass v) => v.MyProperty = "test value";
Func<MyClass, string> compiled = TestAssignment;

// Decompile it
LambdaExpression decompiled = DecompileExtensions.Decompile(compiled);

// The body should be a single assignment expression, not a block
Assert.That(decompiled.Body.NodeType, Is.EqualTo(ExpressionType.Assign));
Assert.That(decompiled.Body.Type, Is.EqualTo(typeof(string)));

// Verify it's a proper assignment expression
var assignment = (BinaryExpression)decompiled.Body;
Assert.That(assignment.Left.NodeType, Is.EqualTo(ExpressionType.MemberAccess));
Assert.That(assignment.Right.NodeType, Is.EqualTo(ExpressionType.Constant));

var constant = (ConstantExpression)assignment.Right;
Assert.That(constant.Value, Is.EqualTo("test value"));
}

[Test]
public void ShouldDecompileMultiplePropertyAssignmentAsAssignmentExpression()
{
// Test with another property to make sure the fix is general
DateTime TestPropertyAssignment(TestClass v) => v.StartDate = new DateTime(2023, 1, 1);
Func<TestClass, DateTime> compiled = TestPropertyAssignment;

// Decompile it
LambdaExpression decompiled = DecompileExtensions.Decompile(compiled);

// The body should be a single assignment expression, not a block
Assert.That(decompiled.Body.NodeType, Is.EqualTo(ExpressionType.Assign));
Assert.That(decompiled.Body.Type, Is.EqualTo(typeof(DateTime)));
}
}
}
6 changes: 3 additions & 3 deletions src/DelegateDecompiler/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("DelegateDecompiler.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c96a47f445d38b9b23cbf52e405743481cb9bd2df4672ad6494abcdeb9daf5588eb6159cc88af5fd5a18cece1f05ecb3ddba4b6329535438f4d173f2b769c4715c136ce65c2f25e7360916737056bca40bee22ab2f178af4c5fdc0e5051a6b2630f31447885eb4f5273271c56bd7b8fb240ab453635a79ec2d8aae4a58a6439f")]
[assembly: InternalsVisibleTo("DelegateDecompiler.EntityFramework, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c96a47f445d38b9b23cbf52e405743481cb9bd2df4672ad6494abcdeb9daf5588eb6159cc88af5fd5a18cece1f05ecb3ddba4b6329535438f4d173f2b769c4715c136ce65c2f25e7360916737056bca40bee22ab2f178af4c5fdc0e5051a6b2630f31447885eb4f5273271c56bd7b8fb240ab453635a79ec2d8aae4a58a6439f")]
[assembly: InternalsVisibleTo("DelegateDecompiler.EntityFrameworkCore5, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c96a47f445d38b9b23cbf52e405743481cb9bd2df4672ad6494abcdeb9daf5588eb6159cc88af5fd5a18cece1f05ecb3ddba4b6329535438f4d173f2b769c4715c136ce65c2f25e7360916737056bca40bee22ab2f178af4c5fdc0e5051a6b2630f31447885eb4f5273271c56bd7b8fb240ab453635a79ec2d8aae4a58a6439f")]
[assembly: InternalsVisibleTo("DelegateDecompiler.Tests")]
[assembly: InternalsVisibleTo("DelegateDecompiler.EntityFramework")]
[assembly: InternalsVisibleTo("DelegateDecompiler.EntityFrameworkCore5")]
25 changes: 25 additions & 0 deletions src/DelegateDecompiler/ProcessorState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,32 @@ public Expression Final()
expressions[i] = Stack.Pop();
}

// Handle C# compiler pattern: assignment expressions that duplicate the assigned value
// The C# compiler generates IL with 'dup' instruction that leaves both the assignment
// and the assigned value on the stack. Since assignment expressions in .NET already
// return the assigned value, we can return just the assignment expression.
if (expressions.Length == 2 &&
expressions[0] is BinaryExpression assignment &&
assignment.NodeType == ExpressionType.Assign &&
IsSameValueAsAssignmentRight(assignment.Right, expressions[1]))
{
return assignment;
}

return Expression.Block(expressions);
}

/// <summary>
/// Determines if the second expression represents the same value as the first expression.
/// This uses reference equality which works for the C# compiler's 'dup' pattern where
/// the exact same expression object is placed on the stack twice.
/// </summary>
private static bool IsSameValueAsAssignmentRight(Expression assignmentRight, Expression stackValue)
{
// Use reference equality as used elsewhere in the codebase (OptimizeExpressionVisitor, Address.cs)
// This works because the C# compiler's 'dup' instruction results in the same expression
// object being referenced in both positions
return assignmentRight == stackValue;
}
}
}
6 changes: 3 additions & 3 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
<PackageProjectUrl>https://github.com/hazzik/DelegateDecompiler</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>

<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>$(SolutionDir)\DelegateDecompiler.snk</AssemblyOriginatorKeyFile>
<!--<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>$(SolutionDir)\DelegateDecompiler.snk</AssemblyOriginatorKeyFile>-->

<FileVersion>$(Version)</FileVersion>

Expand All @@ -33,7 +33,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="GitVersion.MsBuild" Version="6.1.0" PrivateAssets="All" />
<!--<PackageReference Include="GitVersion.MsBuild" Version="6.1.0" PrivateAssets="All" />-->
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="All" />
</ItemGroup>

Expand Down