icsharpcode / ILSpy

.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
21.46k stars 3.35k forks source link

Interpolated string decompilation in .NET 6 #2876

Closed Orden4 closed 1 year ago

Orden4 commented 1 year ago

Is your feature request related to a problem? Please describe. It is although it's a bit convoluted to explain, I'll go in more details on the additional context part. Simply put, interpolated strings in .NET 6 when compiled are rewritten as such:

// original
return $"{this.saveFolder}\\{GetPlayerName(player)}-save-{saveSlot}{this.suffix}.pld"

// compiled
defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(11, 4);
defaultInterpolatedStringHandler.AppendFormatted(saveFolder);
defaultInterpolatedStringHandler.AppendLiteral("\\");
defaultInterpolatedStringHandler.AppendFormatted(Common.GetPlayerName(player));
defaultInterpolatedStringHandler.AppendLiteral("-save-");
defaultInterpolatedStringHandler.AppendFormatted(saveSlot);
defaultInterpolatedStringHandler.AppendFormatted(suffix);
defaultInterpolatedStringHandler.AppendLiteral(".pld");
return defaultInterpolatedStringHandler.ToStringAndClear();

Describe the solution you'd like I would like interpolated strings to be returned to their original format, instead of this rather convoluted and highly verbose format.

Additional context As of a few years ago, Warcraft 3 supports writing maps in Lua. Thanks to that, we now have a whole chain where Drake53 wrote a tool for writing maps in C#, which gets transpiled to Lua using CSharp.lua, and then finally I wrote a library for a ton of useful features to make mapmaking in C# easier. The way my library works is that you import it as a NuGet package, it gets decompiled, then transpiled into Lua. Unfortunately with the move to .NET 6, interpolated strings suddenly get handled in this new way, and CSharp.lua isn't equipped to handle it, since it has no implementation for DefaultInterpolatedStringHandler. I've already raised an issue in the CSharp.lua repo, which is one way to address it, but aside from that Drake53 figured I should raise the issue here as well, because indeed it is rather cumbersome to read this compiled format to begin with, regardless of whether we end up fixing this on another front as well.

Orden4 commented 1 year ago

I realised that I hadn't tried turning off the option to optimize code for release builds, but unfortunately it did not change the outcome.

yanghuan commented 1 year ago

you may set ICSharpCode.Decompiler.DecompilerSettings.StringInterpolation ?

siegfriedpammer commented 1 year ago

Can you check which version of the decompiler engine is used? DefaultInterpolatedStringHandler is only supported in v8.0 preview 2 and later; see the release notes: https://github.com/icsharpcode/ILSpy/releases/tag/v8.0-preview2

Thanks!

Orden4 commented 1 year ago

Ah, apologies, I do recall trying to search for a similar ticket but somehow I did not find anything. War3Net is currently using 7.2.1.6856, I've requested for the version to be upgraded.

I have confirmed that the pre-release version 2 works, but I did run into a bit of an odd behaviour that I figured I'd report. Basically I set up a small project that runs the same code as War3Net did, and because I kind of just lazily told it to look at every directory inside the .nuget folder with a .dll file within it to resolve whatever references it needed. The end result of that was decompiled string interpolation like this:

DefaultInterpolatedStringHandler val = default(DefaultInterpolatedStringHandler);
((DefaultInterpolatedStringHandler)(ref val))..ctor(11, 4);
((DefaultInterpolatedStringHandler)(ref val)).AppendFormatted(saveFolder);
((DefaultInterpolatedStringHandler)(ref val)).AppendLiteral("\\");
((DefaultInterpolatedStringHandler)(ref val)).AppendFormatted(Common.GetPlayerName(player));
((DefaultInterpolatedStringHandler)(ref val)).AppendLiteral("-save-");
((DefaultInterpolatedStringHandler)(ref val)).AppendFormatted<int>(saveSlot);
((DefaultInterpolatedStringHandler)(ref val)).AppendFormatted(suffix);
((DefaultInterpolatedStringHandler)(ref val)).AppendLiteral(".pld");
return ((DefaultInterpolatedStringHandler)(ref val)).ToStringAndClear();

If I only allowed folders containing WCSharp or War3Api (its actual dependencies), the output was the desired $"..." form however.

I don't really know if you consider this a problem, as it's a supremely shoddy way of resolving assemblies, but anyway. I've created a small setup that replicates it for me, although I imagine that mileage may vary depending on the contents of your .nuget folder: DecompileTest.zip (build both before running DecompileTest, then check output.cs in the bin/Debug/net6.0 folder).

dgrunwald commented 1 year ago

If you pick the wrong version of an assembly so that DefaultInterpolatedStringHandler cannot be found by the decompiler, we don't even know if it is a value type or reference type. In that case we don't consider weird output to be a bug.