Skip to content

Commit bb99e96

Browse files
committed
Fix #2115: Re-introduce the evaluation order bug #2050 when a language version <=C# 5 is selected.
1 parent 7f915ad commit bb99e96

File tree

7 files changed

+77
-23
lines changed

7 files changed

+77
-23
lines changed

ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ public void Loops([ValueSource("defaultOptions")] CompilerOptions options)
142142
[Test]
143143
public void NullableTests([ValueSource("defaultOptions")] CompilerOptions options)
144144
{
145-
RunCS(options: options, forceRoslynRecompile: true);
145+
RunCS(options: options);
146146
}
147147

148148
[Test]
@@ -315,7 +315,7 @@ public void MiniJSON([ValueSource("defaultOptions")] CompilerOptions options)
315315
RunCS(options: options);
316316
}
317317

318-
void RunCS([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug, bool forceRoslynRecompile = false)
318+
void RunCS([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug)
319319
{
320320
string testFileName = testName + ".cs";
321321
string testOutputFileName = testName + Tester.GetSuffix(options) + ".exe";
@@ -326,7 +326,7 @@ void RunCS([CallerMemberName] string testName = null, CompilerOptions options =
326326
outputFile = Tester.CompileCSharp(Path.Combine(TestCasePath, testFileName), options,
327327
outputFileName: Path.Combine(TestCasePath, testOutputFileName));
328328
string decompiledCodeFile = Tester.DecompileCSharp(outputFile.PathToAssembly, Tester.GetSettings(options));
329-
if (forceRoslynRecompile || options.HasFlag(CompilerOptions.UseMcs))
329+
if (options.HasFlag(CompilerOptions.UseMcs))
330330
{
331331
// For second pass, use roslyn instead of mcs.
332332
// mcs has some compiler bugs that cause it to not accept ILSpy-generated code,

ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,13 @@ internal static DecompilerSettings GetSettings(CompilerOptions cscOptions)
464464
}
465465
else
466466
{
467-
return new DecompilerSettings(CSharp.LanguageVersion.CSharp5);
467+
var settings= new DecompilerSettings(CSharp.LanguageVersion.CSharp5);
468+
if (cscOptions.HasFlag(CompilerOptions.UseMcs))
469+
{
470+
// we don't recompile with mcs but with roslyn, so we can use ref locals
471+
settings.UseRefLocalsForAccurateOrderOfEvaluation = true;
472+
}
473+
return settings;
468474
}
469475
}
470476

ICSharpCode.Decompiler/DecompilerSettings.cs

+22-2
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ public void SetLanguageVersion(CSharp.LanguageVersion languageVersion)
9292
stringInterpolation = false;
9393
dictionaryInitializers = false;
9494
extensionMethodsInCollectionInitializers = false;
95+
useRefLocalsForAccurateOrderOfEvaluation = false;
9596
}
9697
if (languageVersion < CSharp.LanguageVersion.CSharp7)
9798
{
@@ -153,7 +154,7 @@ public CSharp.LanguageVersion GetMinimumRequiredVersion()
153154
|| discards || localFunctions)
154155
return CSharp.LanguageVersion.CSharp7;
155156
if (awaitInCatchFinally || useExpressionBodyForCalculatedGetterOnlyProperties || nullPropagation
156-
|| stringInterpolation || dictionaryInitializers || extensionMethodsInCollectionInitializers)
157+
|| stringInterpolation || dictionaryInitializers || extensionMethodsInCollectionInitializers || useRefLocalsForAccurateOrderOfEvaluation)
157158
return CSharp.LanguageVersion.CSharp6;
158159
if (asyncAwait)
159160
return CSharp.LanguageVersion.CSharp5;
@@ -445,7 +446,7 @@ public bool LiftNullables {
445446
/// Decompile C# 6 ?. and ?[] operators.
446447
/// </summary>
447448
[Category("C# 6.0 / VS 2015")]
448-
[Description("DecompilerSettings.DecompileAndOperators")]
449+
[Description("DecompilerSettings.NullPropagation")]
449450
public bool NullPropagation {
450451
get { return nullPropagation; }
451452
set {
@@ -819,6 +820,25 @@ public bool ExtensionMethodsInCollectionInitializers {
819820
}
820821
}
821822

823+
bool useRefLocalsForAccurateOrderOfEvaluation = true;
824+
825+
/// <summary>
826+
/// Gets/Sets whether to use C# 6.0 Extension Add methods in collection initializers.
827+
/// Only has an effect if ObjectOrCollectionInitializers is enabled.
828+
/// </summary>
829+
[Category("C# 6.0 / VS 2015")]
830+
[Description("DecompilerSettings.UseRefLocalsForAccurateOrderOfEvaluation")]
831+
public bool UseRefLocalsForAccurateOrderOfEvaluation {
832+
get { return useRefLocalsForAccurateOrderOfEvaluation; }
833+
set {
834+
if (useRefLocalsForAccurateOrderOfEvaluation != value)
835+
{
836+
useRefLocalsForAccurateOrderOfEvaluation = value;
837+
OnPropertyChanged();
838+
}
839+
}
840+
}
841+
822842
bool refExtensionMethods = true;
823843

824844
/// <summary>

ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

+18-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public enum InliningOptions
3333
Aggressive = 1,
3434
IntroduceNamedArguments = 2,
3535
FindDeconstruction = 4,
36+
AllowChangingOrderOfEvaluationForExceptions = 8,
3637
}
3738

3839
/// <summary>
@@ -73,6 +74,10 @@ internal static InliningOptions OptionsForBlock(Block block, int pos, ILTransfor
7374
if (IsInConstructorInitializer(function, inst))
7475
options |= InliningOptions.Aggressive;
7576
}
77+
if (!context.Settings.UseRefLocalsForAccurateOrderOfEvaluation)
78+
{
79+
options |= InliningOptions.AllowChangingOrderOfEvaluationForExceptions;
80+
}
7681
return options;
7782
}
7883

@@ -667,8 +672,19 @@ internal static FindResult FindLoadInNext(ILInstruction expr, ILVariable v, ILIn
667672
// Match found, we can inline
668673
if (expr.SlotInfo == StObj.TargetSlot && !((StObj)expr.Parent).CanInlineIntoTargetSlot(expressionBeingMoved))
669674
{
670-
// special case: the StObj.TargetSlot does not accept some kinds of expressions
671-
return FindResult.Stop;
675+
if ((options & InliningOptions.AllowChangingOrderOfEvaluationForExceptions) != 0)
676+
{
677+
// Intentionally change code semantics so that we can avoid a ref local
678+
if (expressionBeingMoved is LdFlda ldflda)
679+
ldflda.DelayExceptions = true;
680+
else if (expressionBeingMoved is LdElema ldelema)
681+
ldelema.DelayExceptions = true;
682+
}
683+
else
684+
{
685+
// special case: the StObj.TargetSlot does not accept some kinds of expressions
686+
return FindResult.Stop;
687+
}
672688
}
673689
return FindResult.Found(expr);
674690
}

ILSpy/Properties/Resources.Designer.cs

+18-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ILSpy/Properties/Resources.resx

+6-3
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,6 @@ Are you sure you want to continue?</value>
267267
<data name="DecompilerSettings.AsyncEnumerator" xml:space="preserve">
268268
<value>Decompile async IAsyncEnumerator methods</value>
269269
</data>
270-
<data name="DecompilerSettings.DecompileAndOperators" xml:space="preserve">
271-
<value>Decompile ?. and ?[] operators</value>
272-
</data>
273270
<data name="DecompilerSettings.DecompileAnonymousMethodsLambdas" xml:space="preserve">
274271
<value>Decompile anonymous methods/lambdas</value>
275272
</data>
@@ -363,6 +360,9 @@ Are you sure you want to continue?</value>
363360
<data name="DecompilerSettings.NativeIntegers" xml:space="preserve">
364361
<value>Use nint/nuint types</value>
365362
</data>
363+
<data name="DecompilerSettings.NullPropagation" xml:space="preserve">
364+
<value>Decompile ?. and ?[] operators</value>
365+
</data>
366366
<data name="DecompilerSettings.NullableReferenceTypes" xml:space="preserve">
367367
<value>Nullable reference types</value>
368368
</data>
@@ -435,6 +435,9 @@ Are you sure you want to continue?</value>
435435
<data name="DecompilerSettings.UsePatternBasedFixedStatement" xml:space="preserve">
436436
<value>Use pattern-based fixed statement</value>
437437
</data>
438+
<data name="DecompilerSettings.UseRefLocalsForAccurateOrderOfEvaluation" xml:space="preserve">
439+
<value>Use ref locals to accurately represent order of evaluation</value>
440+
</data>
438441
<data name="DecompilerSettings.UseSdkStyleProjectFormat" xml:space="preserve">
439442
<value>Use new SDK style format for generated project files (*.csproj)</value>
440443
</data>

ILSpy/Properties/Resources.zh-Hans.resx

+3-3
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,6 @@
258258
<data name="DecompilerSettings.AsyncEnumerator" xml:space="preserve">
259259
<value>反编译异步 IAsyncEnumerator 方法</value>
260260
</data>
261-
<data name="DecompilerSettings.DecompileAndOperators" xml:space="preserve">
262-
<value>反编译 ?. 和 ?[] 运算符</value>
263-
</data>
264261
<data name="DecompilerSettings.DecompileAnonymousMethodsLambdas" xml:space="preserve">
265262
<value>反编译匿名方法或 lambda</value>
266263
</data>
@@ -348,6 +345,9 @@
348345
<data name="DecompilerSettings.NativeIntegers" xml:space="preserve">
349346
<value>使用 nint/nuint 类型</value>
350347
</data>
348+
<data name="DecompilerSettings.NullPropagation" xml:space="preserve">
349+
<value>反编译 ?. 和 ?[] 运算符</value>
350+
</data>
351351
<data name="DecompilerSettings.NullableReferenceTypes" xml:space="preserve">
352352
<value>可空引用类型</value>
353353
</data>

0 commit comments

Comments
 (0)