Browse Source

Merge remote-tracking branch 'origin/null-propagation'

pull/1066/head
Daniel Grunwald 8 years ago
parent
commit
b2d30dc615
  1. 26
      ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
  2. 11
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  3. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  4. 12
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  5. 29
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/NullPropagation.cs
  6. 213
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs
  7. 863
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.opt.roslyn.il
  8. 1024
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.roslyn.il
  9. 1
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  10. 7
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  11. 20
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  12. 11
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  13. 36
      ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs
  14. 22
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs
  15. 2
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  16. 7
      ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
  17. 15
      ICSharpCode.Decompiler/DecompilerSettings.cs
  18. 2
      ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs
  19. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  20. 12
      ICSharpCode.Decompiler/IL/InstructionFlags.cs
  21. 108
      ICSharpCode.Decompiler/IL/Instructions.cs
  22. 9
      ICSharpCode.Decompiler/IL/Instructions.tt
  23. 9
      ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs
  24. 79
      ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs
  25. 8
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  26. 2
      ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs
  27. 236
      ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs
  28. 13
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs
  29. 34
      ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs
  30. 2
      doc/copyright.txt

26
ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs

@ -54,6 +54,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -54,6 +54,12 @@ namespace ICSharpCode.Decompiler.Tests
CompilerOptions.Optimize | CompilerOptions.UseRoslyn
};
static readonly CompilerOptions[] roslynOnlyOptions =
{
CompilerOptions.UseRoslyn,
CompilerOptions.Optimize | CompilerOptions.UseRoslyn
};
[Test]
public void Comparisons([ValueSource("defaultOptions")] CompilerOptions options)
{
@ -168,6 +174,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -168,6 +174,12 @@ namespace ICSharpCode.Decompiler.Tests
RunCS(options: options);
}
[Test]
public void NullPropagation([ValueSource("roslynOnlyOptions")] CompilerOptions options)
{
RunCS(options: options);
}
[Test]
public void BitNot([Values(false, true)] bool force32Bit)
{
@ -255,7 +267,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -255,7 +267,7 @@ namespace ICSharpCode.Decompiler.Tests
try {
outputFile = Tester.CompileCSharp(Path.Combine(TestCasePath, testFileName), options,
outputFileName: Path.Combine(TestCasePath, testOutputFileName));
string decompiledCodeFile = Tester.DecompileCSharp(outputFile.PathToAssembly, GetSettings(options));
string decompiledCodeFile = Tester.DecompileCSharp(outputFile.PathToAssembly, Tester.GetSettings(options));
decompiledOutputFile = Tester.CompileCSharp(decompiledCodeFile, options);
Tester.RunAndCompareOutput(testFileName, outputFile.PathToAssembly, decompiledOutputFile.PathToAssembly, decompiledCodeFile);
@ -277,7 +289,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -277,7 +289,7 @@ namespace ICSharpCode.Decompiler.Tests
try {
outputFile = Tester.AssembleIL(Path.Combine(TestCasePath, testFileName), asmOptions);
string decompiledCodeFile = Tester.DecompileCSharp(outputFile, GetSettings(options));
string decompiledCodeFile = Tester.DecompileCSharp(outputFile, Tester.GetSettings(options));
decompiledOutputFile = Tester.CompileCSharp(decompiledCodeFile, options);
Tester.RunAndCompareOutput(testFileName, outputFile, decompiledOutputFile.PathToAssembly, decompiledCodeFile);
@ -289,15 +301,5 @@ namespace ICSharpCode.Decompiler.Tests @@ -289,15 +301,5 @@ namespace ICSharpCode.Decompiler.Tests
decompiledOutputFile.TempFiles.Delete();
}
}
DecompilerSettings GetSettings(CompilerOptions options)
{
if (!options.HasFlag(CompilerOptions.UseRoslyn)) {
return new DecompilerSettings {
StringInterpolation = false
};
}
return new DecompilerSettings();
}
}
}

11
ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

@ -238,6 +238,17 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -238,6 +238,17 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
}
}
internal static DecompilerSettings GetSettings(CompilerOptions cscOptions)
{
var settings = new DecompilerSettings();
if ((cscOptions & CompilerOptions.UseRoslyn) == 0) {
// disable C# features not available in legacy compiler
settings.NullPropagation = false;
settings.StringInterpolation = false;
}
return settings;
}
public static CSharpDecompiler GetDecompilerForSnippet(string csharpText)
{
var syntaxTree = SyntaxFactory.ParseSyntaxTree(csharpText);

2
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -68,11 +68,13 @@ @@ -68,11 +68,13 @@
<Compile Include="TestCases\Ugly\NoArrayInitializers.cs" />
<None Include="TestCases\Ugly\NoArrayInitializers.Expected.cs" />
<None Include="TestCases\ILPretty\Issue1047.cs" />
<Compile Include="TestCases\Correctness\NullPropagation.cs" />
<Compile Include="TestCases\ILPretty\Issue982.cs" />
<Compile Include="TestCases\Pretty\AsyncMain.cs" />
<Compile Include="TestCases\Pretty\CS72_PrivateProtected.cs" />
<Compile Include="TestCases\Pretty\CS6_StringInterpolation.cs" />
<Compile Include="TestCases\Pretty\ExpressionTrees.cs" />
<Compile Include="TestCases\Pretty\NullPropagation.cs" />
<Compile Include="TestCases\Pretty\VariableNaming.cs" />
<Compile Include="TestCases\Pretty\VariableNamingWithoutSymbols.cs" />
<Compile Include="Util\SequencePointTests.cs" />

12
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -94,7 +94,9 @@ namespace ICSharpCode.Decompiler.Tests @@ -94,7 +94,9 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void ExceptionHandling([ValueSource("defaultOptions")] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
NullPropagation = false
});
}
[Test]
@ -236,6 +238,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -236,6 +238,12 @@ namespace ICSharpCode.Decompiler.Tests
Run(cscOptions: cscOptions);
}
[Test]
public void NullPropagation([ValueSource("roslynOnlyOptions")] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
void RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, DecompilerSettings decompilerSettings = null)
{
Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, decompilerSettings);
@ -265,7 +273,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -265,7 +273,7 @@ namespace ICSharpCode.Decompiler.Tests
}
var executable = Tester.AssembleIL(ilFile, asmOptions);
var decompiled = Tester.DecompileCSharp(executable, decompilerSettings);
var decompiled = Tester.DecompileCSharp(executable, decompilerSettings ?? Tester.GetSettings(cscOptions));
CodeAssert.FilesAreEqual(csFile, decompiled, Tester.GetPreprocessorSymbols(cscOptions).ToArray());
}

29
ICSharpCode.Decompiler.Tests/TestCases/Correctness/NullPropagation.cs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
class NullPropagation
{
static void Main()
{
new NullPropagation().TestNotCoalescing();
}
class MyClass
{
public string Text;
}
void TestNotCoalescing()
{
Console.WriteLine("TestNotCoalescing:");
Console.WriteLine(NotCoalescing(null));
Console.WriteLine(NotCoalescing(new MyClass()));
}
string NotCoalescing(MyClass c)
{
return c != null ? c.Text : "Hello";
}
}
}

213
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs

@ -0,0 +1,213 @@ @@ -0,0 +1,213 @@
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal class NullPropagation
{
private class MyClass
{
public int IntVal;
public string Text;
public MyClass Field;
public MyClass Property {
get;
set;
}
public MyClass this[int index] {
get {
return null;
}
}
public MyClass Method(int arg)
{
return null;
}
public void Done()
{
}
}
private struct MyStruct
{
public int IntVal;
public MyClass Field;
public MyStruct? Property1 {
get {
return null;
}
}
public MyStruct Property2 {
get {
return default(MyStruct);
}
}
public MyStruct? this[int index] {
get {
return null;
}
}
public MyStruct? Method1(int arg)
{
return null;
}
public MyStruct Method2(int arg)
{
return default(MyStruct);
}
public void Done()
{
}
}
private int GetInt()
{
return 9;
}
private string GetString()
{
return null;
}
private MyClass GetMyClass()
{
return null;
}
private MyStruct? GetMyStruct()
{
return null;
}
public string Substring()
{
return this.GetString()?.Substring(this.GetInt());
}
public void CallSubstringAndIgnoreResult()
{
this.GetString()?.Substring(this.GetInt());
}
private void Use<T>(T t)
{
}
public void CallDone()
{
this.GetMyClass()?.Done();
this.GetMyClass()?.Field?.Done();
this.GetMyClass()?.Field.Done();
this.GetMyClass()?.Property?.Done();
this.GetMyClass()?.Property.Done();
this.GetMyClass()?.Method(this.GetInt())?.Done();
this.GetMyClass()?.Method(this.GetInt()).Done();
this.GetMyClass()?[this.GetInt()]?.Done();
this.GetMyClass()?[this.GetInt()].Done();
}
public void CallDoneStruct()
{
this.GetMyStruct()?.Done();
#if STRUCT_SPLITTING_IMPROVED
this.GetMyStruct()?.Field?.Done();
this.GetMyStruct()?.Field.Done();
this.GetMyStruct()?.Property1?.Done();
this.GetMyStruct()?.Property2.Done();
this.GetMyStruct()?.Method1(this.GetInt())?.Done();
this.GetMyStruct()?.Method2(this.GetInt()).Done();
this.GetMyStruct()?[this.GetInt()]?.Done();
#endif
}
public void RequiredParentheses()
{
(this.GetMyClass()?.Field).Done();
(this.GetMyClass()?.Method(this.GetInt())).Done();
#if STRUCT_SPLITTING_IMPROVED
(GetMyStruct()?.Property2)?.Done();
#endif
}
public int?[] ChainsOnClass()
{
return new int?[9] {
this.GetMyClass()?.IntVal,
this.GetMyClass()?.Field.IntVal,
this.GetMyClass()?.Field?.IntVal,
this.GetMyClass()?.Property.IntVal,
this.GetMyClass()?.Property?.IntVal,
this.GetMyClass()?.Method(this.GetInt()).IntVal,
this.GetMyClass()?.Method(this.GetInt())?.IntVal,
this.GetMyClass()?[this.GetInt()].IntVal,
this.GetMyClass()?[this.GetInt()]?.IntVal
};
}
#if STRUCT_SPLITTING_IMPROVED
public int?[] ChainsStruct()
{
return new int?[8] {
this.GetMyStruct()?.IntVal,
this.GetMyStruct()?.Field.IntVal,
this.GetMyStruct()?.Field?.IntVal,
this.GetMyStruct()?.Property2.IntVal,
this.GetMyStruct()?.Property1?.IntVal,
this.GetMyStruct()?.Method2(this.GetInt()).IntVal,
this.GetMyStruct()?.Method1(this.GetInt())?.IntVal,
this.GetMyStruct()?[this.GetInt()]?.IntVal
};
}
#endif
public int CoalescingReturn()
{
return this.GetMyClass()?.IntVal ?? 1;
}
public void Coalescing()
{
this.Use(this.GetMyClass()?.IntVal ?? 1);
}
public void CoalescingString()
{
this.Use(this.GetMyClass()?.Text ?? "Hello");
}
public void InvokeDelegate(EventHandler eh)
{
eh?.Invoke(null, EventArgs.Empty);
}
public int? InvokeDelegate(Func<int> f)
{
return f?.Invoke();
}
private void NotNullPropagation(MyClass c)
{
// don't decompile this to "(c?.IntVal ?? 0) != 0"
if (c != null && c.IntVal != 0) {
Console.WriteLine("non-zero");
}
if (c == null || c.IntVal == 0) {
Console.WriteLine("null or zero");
}
Console.WriteLine("end of method");
}
private void Setter(MyClass c)
{
if (c != null) {
c.IntVal = 1;
}
Console.WriteLine();
if (c != null) {
c.Property = null;
}
}
}
}

863
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.opt.roslyn.il

@ -0,0 +1,863 @@ @@ -0,0 +1,863 @@
// Microsoft (R) .NET Framework IL Disassembler. Version 4.6.1055.0
// Copyright (c) Microsoft Corporation. All rights reserved.
// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly NullPropagation
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
// --- The following custom attribute is added automatically, do not uncomment -------
// .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 02 00 00 00 00 00 )
.permissionset reqmin
= {[mscorlib]System.Security.Permissions.SecurityPermissionAttribute = {property bool 'SkipVerification' = bool(true)}}
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module NullPropagation.dll
// MVID: {DDAB2C82-C901-450A-B55D-2D0D7BC34C48}
.custom instance void [mscorlib]System.Security.UnverifiableCodeAttribute::.ctor() = ( 01 00 00 00 )
.imagebase 0x10000000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x003E0000
// =============== CLASS MEMBERS DECLARATION ===================
.class private auto ansi beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation
extends [mscorlib]System.Object
{
.class auto ansi nested private beforefieldinit MyClass
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6D 00 00 ) // ...Item..
.field public int32 IntVal
.field public string Text
.field public class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass Field
.field private class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass '<Property>k__BackingField'
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
.method public hidebysig specialname
instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass
get_Property() cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::'<Property>k__BackingField'
IL_0006: ret
} // end of method MyClass::get_Property
.method public hidebysig specialname
instance void set_Property(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass 'value') cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::'<Property>k__BackingField'
IL_0007: ret
} // end of method MyClass::set_Property
.method public hidebysig specialname
instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass
get_Item(int32 index) cil managed
{
// Code size 2 (0x2)
.maxstack 8
IL_0000: ldnull
IL_0001: ret
} // end of method MyClass::get_Item
.method public hidebysig instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass
Method(int32 arg) cil managed
{
// Code size 2 (0x2)
.maxstack 8
IL_0000: ldnull
IL_0001: ret
} // end of method MyClass::Method
.method public hidebysig instance void
Done() cil managed
{
// Code size 1 (0x1)
.maxstack 8
IL_0000: ret
} // end of method MyClass::Done
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method MyClass::.ctor
.property instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass
Property()
{
.get instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Property()
.set instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::set_Property(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass)
} // end of property MyClass::Property
.property instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass
Item(int32)
{
.get instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Item(int32)
} // end of property MyClass::Item
} // end of class MyClass
.class sequential ansi sealed nested private beforefieldinit MyStruct
extends [mscorlib]System.ValueType
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 04 49 74 65 6D 00 00 ) // ...Item..
.field public int32 IntVal
.field public class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass Field
.method public hidebysig specialname
instance valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct>
get_Property1() cil managed
{
// Code size 10 (0xa)
.maxstack 1
.locals init (valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct> V_0)
IL_0000: ldloca.s V_0
IL_0002: initobj valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct>
IL_0008: ldloc.0
IL_0009: ret
} // end of method MyStruct::get_Property1
.method public hidebysig specialname
instance valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct
get_Property2() cil managed
{
// Code size 10 (0xa)
.maxstack 1
.locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct V_0)
IL_0000: ldloca.s V_0
IL_0002: initobj ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct
IL_0008: ldloc.0
IL_0009: ret
} // end of method MyStruct::get_Property2
.method public hidebysig specialname
instance valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct>
get_Item(int32 index) cil managed
{
// Code size 10 (0xa)
.maxstack 1
.locals init (valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct> V_0)
IL_0000: ldloca.s V_0
IL_0002: initobj valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct>
IL_0008: ldloc.0
IL_0009: ret
} // end of method MyStruct::get_Item
.method public hidebysig instance valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct>
Method1(int32 arg) cil managed
{
// Code size 10 (0xa)
.maxstack 1
.locals init (valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct> V_0)
IL_0000: ldloca.s V_0
IL_0002: initobj valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct>
IL_0008: ldloc.0
IL_0009: ret
} // end of method MyStruct::Method1
.method public hidebysig instance valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct
Method2(int32 arg) cil managed
{
// Code size 10 (0xa)
.maxstack 1
.locals init (valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct V_0)
IL_0000: ldloca.s V_0
IL_0002: initobj ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct
IL_0008: ldloc.0
IL_0009: ret
} // end of method MyStruct::Method2
.method public hidebysig instance void
Done() cil managed
{
// Code size 1 (0x1)
.maxstack 8
IL_0000: ret
} // end of method MyStruct::Done
.property instance valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct>
Property1()
{
.get instance valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct> ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct::get_Property1()
} // end of property MyStruct::Property1
.property instance valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct
Property2()
{
.get instance valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct::get_Property2()
} // end of property MyStruct::Property2
.property instance valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct>
Item(int32)
{
.get instance valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct> ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct::get_Item(int32)
} // end of property MyStruct::Item
} // end of class MyStruct
.method private hidebysig instance int32
GetInt() cil managed
{
// Code size 3 (0x3)
.maxstack 8
IL_0000: ldc.i4.s 9
IL_0002: ret
} // end of method NullPropagation::GetInt
.method private hidebysig instance string
GetString() cil managed
{
// Code size 2 (0x2)
.maxstack 8
IL_0000: ldnull
IL_0001: ret
} // end of method NullPropagation::GetString
.method private hidebysig instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass
GetMyClass() cil managed
{
// Code size 2 (0x2)
.maxstack 8
IL_0000: ldnull
IL_0001: ret
} // end of method NullPropagation::GetMyClass
.method private hidebysig instance valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct>
GetMyStruct() cil managed
{
// Code size 10 (0xa)
.maxstack 1
.locals init (valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct> V_0)
IL_0000: ldloca.s V_0
IL_0002: initobj valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct>
IL_0008: ldloc.0
IL_0009: ret
} // end of method NullPropagation::GetMyStruct
.method public hidebysig instance string
Substring() cil managed
{
// Code size 24 (0x18)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetString()
IL_0006: dup
IL_0007: brtrue.s IL_000c
IL_0009: pop
IL_000a: ldnull
IL_000b: ret
IL_000c: ldarg.0
IL_000d: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt()
IL_0012: call instance string [mscorlib]System.String::Substring(int32)
IL_0017: ret
} // end of method NullPropagation::Substring
.method public hidebysig instance void
CallSubstringAndIgnoreResult() cil managed
{
// Code size 24 (0x18)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance string ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetString()
IL_0006: dup
IL_0007: brtrue.s IL_000b
IL_0009: pop
IL_000a: ret
IL_000b: ldarg.0
IL_000c: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt()
IL_0011: call instance string [mscorlib]System.String::Substring(int32)
IL_0016: pop
IL_0017: ret
} // end of method NullPropagation::CallSubstringAndIgnoreResult
.method private hidebysig instance void
Use<T>(!!T t) cil managed
{
// Code size 1 (0x1)
.maxstack 8
IL_0000: ret
} // end of method NullPropagation::Use
.method public hidebysig instance void
CallDone() cil managed
{
// Code size 241 (0xf1)
.maxstack 2
IL_0000: ldarg.0
IL_0001: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_0006: dup
IL_0007: brtrue.s IL_000c
IL_0009: pop
IL_000a: br.s IL_0011
IL_000c: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done()
IL_0011: ldarg.0
IL_0012: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_0017: dup
IL_0018: brtrue.s IL_001d
IL_001a: pop
IL_001b: br.s IL_002d
IL_001d: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Field
IL_0022: dup
IL_0023: brtrue.s IL_0028
IL_0025: pop
IL_0026: br.s IL_002d
IL_0028: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done()
IL_002d: ldarg.0
IL_002e: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_0033: dup
IL_0034: brtrue.s IL_0039
IL_0036: pop
IL_0037: br.s IL_0043
IL_0039: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Field
IL_003e: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done()
IL_0043: ldarg.0
IL_0044: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_0049: dup
IL_004a: brtrue.s IL_004f
IL_004c: pop
IL_004d: br.s IL_005f
IL_004f: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Property()
IL_0054: dup
IL_0055: brtrue.s IL_005a
IL_0057: pop
IL_0058: br.s IL_005f
IL_005a: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done()
IL_005f: ldarg.0
IL_0060: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_0065: dup
IL_0066: brtrue.s IL_006b
IL_0068: pop
IL_0069: br.s IL_0075
IL_006b: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Property()
IL_0070: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done()
IL_0075: ldarg.0
IL_0076: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_007b: dup
IL_007c: brtrue.s IL_0081
IL_007e: pop
IL_007f: br.s IL_0097
IL_0081: ldarg.0
IL_0082: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt()
IL_0087: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Method(int32)
IL_008c: dup
IL_008d: brtrue.s IL_0092
IL_008f: pop
IL_0090: br.s IL_0097
IL_0092: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done()
IL_0097: ldarg.0
IL_0098: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_009d: dup
IL_009e: brtrue.s IL_00a3
IL_00a0: pop
IL_00a1: br.s IL_00b3
IL_00a3: ldarg.0
IL_00a4: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt()
IL_00a9: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Method(int32)
IL_00ae: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done()
IL_00b3: ldarg.0
IL_00b4: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_00b9: dup
IL_00ba: brtrue.s IL_00bf
IL_00bc: pop
IL_00bd: br.s IL_00d5
IL_00bf: ldarg.0
IL_00c0: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt()
IL_00c5: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Item(int32)
IL_00ca: dup
IL_00cb: brtrue.s IL_00d0
IL_00cd: pop
IL_00ce: br.s IL_00d5
IL_00d0: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done()
IL_00d5: ldarg.0
IL_00d6: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_00db: dup
IL_00dc: brtrue.s IL_00e0
IL_00de: pop
IL_00df: ret
IL_00e0: ldarg.0
IL_00e1: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt()
IL_00e6: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Item(int32)
IL_00eb: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done()
IL_00f0: ret
} // end of method NullPropagation::CallDone
.method public hidebysig instance void
CallDoneStruct() cil managed
{
// Code size 33 (0x21)
.maxstack 2
.locals init (valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct> V_0,
valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct V_1)
IL_0000: ldarg.0
IL_0001: call instance valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct> ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyStruct()
IL_0006: stloc.0
IL_0007: ldloca.s V_0
IL_0009: dup
IL_000a: call instance bool valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct>::get_HasValue()
IL_000f: brtrue.s IL_0013
IL_0011: pop
IL_0012: ret
IL_0013: call instance !0 valuetype [mscorlib]System.Nullable`1<valuetype ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct>::GetValueOrDefault()
IL_0018: stloc.1
IL_0019: ldloca.s V_1
IL_001b: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyStruct::Done()
IL_0020: ret
} // end of method NullPropagation::CallDoneStruct
.method public hidebysig instance void
RequiredParentheses() cil managed
{
// Code size 53 (0x35)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_0006: dup
IL_0007: brtrue.s IL_000d
IL_0009: pop
IL_000a: ldnull
IL_000b: br.s IL_0012
IL_000d: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Field
IL_0012: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done()
IL_0017: ldarg.0
IL_0018: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_001d: dup
IL_001e: brtrue.s IL_0024
IL_0020: pop
IL_0021: ldnull
IL_0022: br.s IL_002f
IL_0024: ldarg.0
IL_0025: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt()
IL_002a: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Method(int32)
IL_002f: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Done()
IL_0034: ret
} // end of method NullPropagation::RequiredParentheses
.method public hidebysig instance valuetype [mscorlib]System.Nullable`1<int32>[]
ChainsOnClass() cil managed
{
// Code size 474 (0x1da)
.maxstack 5
.locals init (valuetype [mscorlib]System.Nullable`1<int32> V_0)
IL_0000: ldc.i4.s 9
IL_0002: newarr valuetype [mscorlib]System.Nullable`1<int32>
IL_0007: dup
IL_0008: ldc.i4.0
IL_0009: ldarg.0
IL_000a: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_000f: dup
IL_0010: brtrue.s IL_001e
IL_0012: pop
IL_0013: ldloca.s V_0
IL_0015: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_001b: ldloc.0
IL_001c: br.s IL_0028
IL_001e: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal
IL_0023: newobj instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
IL_0028: stelem valuetype [mscorlib]System.Nullable`1<int32>
IL_002d: dup
IL_002e: ldc.i4.1
IL_002f: ldarg.0
IL_0030: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_0035: dup
IL_0036: brtrue.s IL_0044
IL_0038: pop
IL_0039: ldloca.s V_0
IL_003b: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_0041: ldloc.0
IL_0042: br.s IL_0053
IL_0044: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Field
IL_0049: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal
IL_004e: newobj instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
IL_0053: stelem valuetype [mscorlib]System.Nullable`1<int32>
IL_0058: dup
IL_0059: ldc.i4.2
IL_005a: ldarg.0
IL_005b: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_0060: dup
IL_0061: brtrue.s IL_006f
IL_0063: pop
IL_0064: ldloca.s V_0
IL_0066: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_006c: ldloc.0
IL_006d: br.s IL_008d
IL_006f: ldfld class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Field
IL_0074: dup
IL_0075: brtrue.s IL_0083
IL_0077: pop
IL_0078: ldloca.s V_0
IL_007a: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_0080: ldloc.0
IL_0081: br.s IL_008d
IL_0083: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal
IL_0088: newobj instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
IL_008d: stelem valuetype [mscorlib]System.Nullable`1<int32>
IL_0092: dup
IL_0093: ldc.i4.3
IL_0094: ldarg.0
IL_0095: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_009a: dup
IL_009b: brtrue.s IL_00a9
IL_009d: pop
IL_009e: ldloca.s V_0
IL_00a0: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_00a6: ldloc.0
IL_00a7: br.s IL_00b8
IL_00a9: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Property()
IL_00ae: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal
IL_00b3: newobj instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
IL_00b8: stelem valuetype [mscorlib]System.Nullable`1<int32>
IL_00bd: dup
IL_00be: ldc.i4.4
IL_00bf: ldarg.0
IL_00c0: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_00c5: dup
IL_00c6: brtrue.s IL_00d4
IL_00c8: pop
IL_00c9: ldloca.s V_0
IL_00cb: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_00d1: ldloc.0
IL_00d2: br.s IL_00f2
IL_00d4: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Property()
IL_00d9: dup
IL_00da: brtrue.s IL_00e8
IL_00dc: pop
IL_00dd: ldloca.s V_0
IL_00df: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_00e5: ldloc.0
IL_00e6: br.s IL_00f2
IL_00e8: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal
IL_00ed: newobj instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
IL_00f2: stelem valuetype [mscorlib]System.Nullable`1<int32>
IL_00f7: dup
IL_00f8: ldc.i4.5
IL_00f9: ldarg.0
IL_00fa: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_00ff: dup
IL_0100: brtrue.s IL_010e
IL_0102: pop
IL_0103: ldloca.s V_0
IL_0105: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_010b: ldloc.0
IL_010c: br.s IL_0123
IL_010e: ldarg.0
IL_010f: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt()
IL_0114: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Method(int32)
IL_0119: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal
IL_011e: newobj instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
IL_0123: stelem valuetype [mscorlib]System.Nullable`1<int32>
IL_0128: dup
IL_0129: ldc.i4.6
IL_012a: ldarg.0
IL_012b: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_0130: dup
IL_0131: brtrue.s IL_013f
IL_0133: pop
IL_0134: ldloca.s V_0
IL_0136: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_013c: ldloc.0
IL_013d: br.s IL_0163
IL_013f: ldarg.0
IL_0140: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt()
IL_0145: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Method(int32)
IL_014a: dup
IL_014b: brtrue.s IL_0159
IL_014d: pop
IL_014e: ldloca.s V_0
IL_0150: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_0156: ldloc.0
IL_0157: br.s IL_0163
IL_0159: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal
IL_015e: newobj instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
IL_0163: stelem valuetype [mscorlib]System.Nullable`1<int32>
IL_0168: dup
IL_0169: ldc.i4.7
IL_016a: ldarg.0
IL_016b: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_0170: dup
IL_0171: brtrue.s IL_017f
IL_0173: pop
IL_0174: ldloca.s V_0
IL_0176: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_017c: ldloc.0
IL_017d: br.s IL_0194
IL_017f: ldarg.0
IL_0180: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt()
IL_0185: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Item(int32)
IL_018a: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal
IL_018f: newobj instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
IL_0194: stelem valuetype [mscorlib]System.Nullable`1<int32>
IL_0199: dup
IL_019a: ldc.i4.8
IL_019b: ldarg.0
IL_019c: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_01a1: dup
IL_01a2: brtrue.s IL_01b0
IL_01a4: pop
IL_01a5: ldloca.s V_0
IL_01a7: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_01ad: ldloc.0
IL_01ae: br.s IL_01d4
IL_01b0: ldarg.0
IL_01b1: call instance int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetInt()
IL_01b6: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::get_Item(int32)
IL_01bb: dup
IL_01bc: brtrue.s IL_01ca
IL_01be: pop
IL_01bf: ldloca.s V_0
IL_01c1: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_01c7: ldloc.0
IL_01c8: br.s IL_01d4
IL_01ca: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal
IL_01cf: newobj instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
IL_01d4: stelem valuetype [mscorlib]System.Nullable`1<int32>
IL_01d9: ret
} // end of method NullPropagation::ChainsOnClass
.method public hidebysig instance int32
CoalescingReturn() cil managed
{
// Code size 18 (0x12)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_0006: dup
IL_0007: brtrue.s IL_000c
IL_0009: pop
IL_000a: ldc.i4.1
IL_000b: ret
IL_000c: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal
IL_0011: ret
} // end of method NullPropagation::CoalescingReturn
.method public hidebysig instance void
Coalescing() cil managed
{
// Code size 25 (0x19)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.0
IL_0002: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_0007: dup
IL_0008: brtrue.s IL_000e
IL_000a: pop
IL_000b: ldc.i4.1
IL_000c: br.s IL_0013
IL_000e: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal
IL_0013: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::Use<int32>(!!0)
IL_0018: ret
} // end of method NullPropagation::Coalescing
.method public hidebysig instance void
CoalescingString() cil managed
{
// Code size 34 (0x22)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldarg.0
IL_0002: call instance class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::GetMyClass()
IL_0007: dup
IL_0008: brtrue.s IL_000e
IL_000a: pop
IL_000b: ldnull
IL_000c: br.s IL_0013
IL_000e: ldfld string ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::Text
IL_0013: dup
IL_0014: brtrue.s IL_001c
IL_0016: pop
IL_0017: ldstr "Hello"
IL_001c: call instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation::Use<string>(!!0)
IL_0021: ret
} // end of method NullPropagation::CoalescingString
.method public hidebysig instance void
InvokeDelegate(class [mscorlib]System.EventHandler eh) cil managed
{
// Code size 16 (0x10)
.maxstack 8
IL_0000: ldarg.1
IL_0001: brfalse.s IL_000f
IL_0003: ldarg.1
IL_0004: ldnull
IL_0005: ldsfld class [mscorlib]System.EventArgs [mscorlib]System.EventArgs::Empty
IL_000a: callvirt instance void [mscorlib]System.EventHandler::Invoke(object,
class [mscorlib]System.EventArgs)
IL_000f: ret
} // end of method NullPropagation::InvokeDelegate
.method public hidebysig instance valuetype [mscorlib]System.Nullable`1<int32>
InvokeDelegate(class [mscorlib]System.Func`1<int32> f) cil managed
{
// Code size 25 (0x19)
.maxstack 1
.locals init (valuetype [mscorlib]System.Nullable`1<int32> V_0)
IL_0000: ldarg.1
IL_0001: brtrue.s IL_000d
IL_0003: ldloca.s V_0
IL_0005: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_000b: ldloc.0
IL_000c: ret
IL_000d: ldarg.1
IL_000e: callvirt instance !0 class [mscorlib]System.Func`1<int32>::Invoke()
IL_0013: newobj instance void valuetype [mscorlib]System.Nullable`1<int32>::.ctor(!0)
IL_0018: ret
} // end of method NullPropagation::InvokeDelegate
.method private hidebysig instance void
NotNullPropagation(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass c) cil managed
{
// Code size 53 (0x35)
.maxstack 8
IL_0000: ldarg.1
IL_0001: brfalse.s IL_0015
IL_0003: ldarg.1
IL_0004: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal
IL_0009: brfalse.s IL_0015
IL_000b: ldstr "non-zero"
IL_0010: call void [mscorlib]System.Console::WriteLine(string)
IL_0015: ldarg.1
IL_0016: brfalse.s IL_0020
IL_0018: ldarg.1
IL_0019: ldfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal
IL_001e: brtrue.s IL_002a
IL_0020: ldstr "null or zero"
IL_0025: call void [mscorlib]System.Console::WriteLine(string)
IL_002a: ldstr "end of method"
IL_002f: call void [mscorlib]System.Console::WriteLine(string)
IL_0034: ret
} // end of method NullPropagation::NotNullPropagation
.method private hidebysig instance void
Setter(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass c) cil managed
{
// Code size 26 (0x1a)
.maxstack 8
IL_0000: ldarg.1
IL_0001: brfalse.s IL_000a
IL_0003: ldarg.1
IL_0004: ldc.i4.1
IL_0005: stfld int32 ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::IntVal
IL_000a: call void [mscorlib]System.Console::WriteLine()
IL_000f: ldarg.1
IL_0010: brfalse.s IL_0019
IL_0012: ldarg.1
IL_0013: ldnull
IL_0014: callvirt instance void ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass::set_Property(class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation/MyClass)
IL_0019: ret
} // end of method NullPropagation::Setter
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method NullPropagation::.ctor
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Pretty.NullPropagation
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************

1024
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.roslyn.il

File diff suppressed because it is too large Load Diff

1
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -127,6 +127,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -127,6 +127,7 @@ namespace ICSharpCode.Decompiler.CSharp
new TransformAssignment(), // inline and compound assignments
new NullCoalescingTransform(),
new NullableLiftingStatementTransform(),
new NullPropagationStatementTransform(),
new TransformArrayInitializers(),
new TransformCollectionAndObjectInitializers(),
new TransformExpressionTrees()

7
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -188,7 +188,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -188,7 +188,7 @@ namespace ICSharpCode.Decompiler.CSharp
int allowedParamCount = (method.ReturnType.IsKnownType(KnownTypeCode.Void) ? 1 : 0);
if (method.IsAccessor && (method.AccessorOwner.SymbolKind == SymbolKind.Indexer || expectedParameters.Count == allowedParamCount)) {
return HandleAccessorCall(expectedTargetDetails, method, target, arguments.ToList());
} else if (method.Name == "Invoke" && method.DeclaringType.Kind == TypeKind.Delegate) {
} else if (method.Name == "Invoke" && method.DeclaringType.Kind == TypeKind.Delegate && !IsNullConditional(target)) {
return new InvocationExpression(target, arguments.Select(arg => arg.Expression)).WithRR(rr);
} else if (IsDelegateEqualityComparison(method, arguments)) {
return HandleDelegateEqualityComparison(method, arguments)
@ -252,6 +252,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -252,6 +252,11 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
static bool IsNullConditional(Expression expr)
{
return expr is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.NullConditional;
}
private void ModifyReturnTypeOfLambda(LambdaExpression lambda)
{
var resolveResult = (DecompiledLambdaResolveResult)lambda.GetResolveResult();

20
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -2145,6 +2145,26 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2145,6 +2145,26 @@ namespace ICSharpCode.Decompiler.CSharp
.WithRR(new ResolveResult(inst.GetResultMethod?.ReturnType ?? SpecialType.UnknownType));
}
protected internal override TranslatedExpression VisitNullableRewrap(NullableRewrap inst, TranslationContext context)
{
var arg = Translate(inst.Argument);
IType type = arg.Type;
if (NullableType.IsNonNullableValueType(type)) {
type = NullableType.Create(compilation, type);
}
return new UnaryOperatorExpression(UnaryOperatorType.NullConditionalRewrap, arg)
.WithILInstruction(inst)
.WithRR(new ResolveResult(type));
}
protected internal override TranslatedExpression VisitNullableUnwrap(NullableUnwrap inst, TranslationContext context)
{
var arg = Translate(inst.Argument);
return new UnaryOperatorExpression(UnaryOperatorType.NullConditional, arg)
.WithILInstruction(inst)
.WithRR(new ResolveResult(NullableType.GetUnderlyingType(arg.Type)));
}
protected internal override TranslatedExpression VisitInvalidBranch(InvalidBranch inst, TranslationContext context)
{
string message = "Error";

11
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -1085,16 +1085,23 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1085,16 +1085,23 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
var opSymbol = UnaryOperatorExpression.GetOperatorRole(opType);
if (opType == UnaryOperatorType.Await) {
WriteKeyword(opSymbol);
} else if (!(opType == UnaryOperatorType.PostIncrement || opType == UnaryOperatorType.PostDecrement)) {
} else if (!IsPostfixOperator(opType) && opType != UnaryOperatorType.NullConditionalRewrap) {
WriteToken(opSymbol);
}
unaryOperatorExpression.Expression.AcceptVisitor(this);
if (opType == UnaryOperatorType.PostIncrement || opType == UnaryOperatorType.PostDecrement) {
if (IsPostfixOperator(opType)) {
WriteToken(opSymbol);
}
EndNode(unaryOperatorExpression);
}
static bool IsPostfixOperator(UnaryOperatorType op)
{
return op == UnaryOperatorType.PostIncrement
|| op == UnaryOperatorType.PostDecrement
|| op == UnaryOperatorType.NullConditional;
}
public virtual void VisitUncheckedExpression(UncheckedExpression uncheckedExpression)
{
StartNode(uncheckedExpression);

36
ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs

@ -36,7 +36,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -36,7 +36,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
/// </summary>
public bool InsertParenthesesForReadability { get; set; }
const int Primary = 16;
const int Primary = 17;
const int NullableRewrap = 16;
const int QueryOrLambda = 15;
const int Unary = 14;
const int RelationalAndTypeTesting = 10;
@ -55,12 +56,17 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -55,12 +56,17 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
// primary expressions get parenthesized.
return QueryOrLambda;
}
UnaryOperatorExpression uoe = expr as UnaryOperatorExpression;
if (uoe != null) {
if (uoe.Operator == UnaryOperatorType.PostDecrement || uoe.Operator == UnaryOperatorType.PostIncrement)
return Primary;
else
return Unary;
if (expr is UnaryOperatorExpression uoe) {
switch (uoe.Operator) {
case UnaryOperatorType.PostDecrement:
case UnaryOperatorType.PostIncrement:
case UnaryOperatorType.NullConditional:
return Primary;
case UnaryOperatorType.NullConditionalRewrap:
return NullableRewrap;
default:
return Unary;
}
}
if (expr is CastExpression)
return Unary;
@ -177,7 +183,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -177,7 +183,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{
// Even in readability mode, don't parenthesize casts of casts.
if (!(castExpression.Expression is CastExpression)) {
ParenthesizeIfRequired(castExpression.Expression, InsertParenthesesForReadability ? Primary : Unary);
ParenthesizeIfRequired(castExpression.Expression, InsertParenthesesForReadability ? NullableRewrap : Unary);
}
// There's a nasty issue in the C# grammar: cast expressions including certain operators are ambiguous in some cases
// "(int)-1" is fine, but "(A)-b" is not a cast.
@ -243,11 +249,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -243,11 +249,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
int precedence = GetPrecedence(binaryOperatorExpression);
if (binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing) {
if (InsertParenthesesForReadability) {
ParenthesizeIfRequired(binaryOperatorExpression.Left, Primary);
ParenthesizeIfRequired(binaryOperatorExpression.Left, NullableRewrap);
if (GetBinaryOperatorType(binaryOperatorExpression.Right) == BinaryOperatorType.NullCoalescing) {
ParenthesizeIfRequired(binaryOperatorExpression.Right, precedence);
} else {
ParenthesizeIfRequired(binaryOperatorExpression.Right, Primary);
ParenthesizeIfRequired(binaryOperatorExpression.Right, NullableRewrap);
}
} else {
// ?? is right-associative
@ -286,7 +292,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -286,7 +292,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{
if (InsertParenthesesForReadability) {
// few people know the precedence of 'is', so always put parentheses in nice-looking mode.
ParenthesizeIfRequired(isExpression.Expression, Primary);
ParenthesizeIfRequired(isExpression.Expression, NullableRewrap);
} else {
ParenthesizeIfRequired(isExpression.Expression, RelationalAndTypeTesting);
}
@ -297,7 +303,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -297,7 +303,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{
if (InsertParenthesesForReadability) {
// few people know the precedence of 'as', so always put parentheses in nice-looking mode.
ParenthesizeIfRequired(asExpression.Expression, Primary);
ParenthesizeIfRequired(asExpression.Expression, NullableRewrap);
} else {
ParenthesizeIfRequired(asExpression.Expression, RelationalAndTypeTesting);
}
@ -313,9 +319,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -313,9 +319,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
// Only ((a ? b : c) ? d : e) strictly needs the additional parentheses
if (InsertParenthesesForReadability) {
// Precedence of ?: can be confusing; so always put parentheses in nice-looking mode.
ParenthesizeIfRequired(conditionalExpression.Condition, Primary);
ParenthesizeIfRequired(conditionalExpression.TrueExpression, Primary);
ParenthesizeIfRequired(conditionalExpression.FalseExpression, Primary);
ParenthesizeIfRequired(conditionalExpression.Condition, NullableRewrap);
ParenthesizeIfRequired(conditionalExpression.TrueExpression, NullableRewrap);
ParenthesizeIfRequired(conditionalExpression.FalseExpression, NullableRewrap);
} else {
ParenthesizeIfRequired(conditionalExpression.Condition, Conditional + 1);
ParenthesizeIfRequired(conditionalExpression.TrueExpression, Conditional);

22
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs

@ -43,6 +43,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -43,6 +43,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public readonly static TokenRole DereferenceRole = new TokenRole ("*");
public readonly static TokenRole AddressOfRole = new TokenRole ("&");
public readonly static TokenRole AwaitRole = new TokenRole ("await");
public readonly static TokenRole NullConditionalRole = new TokenRole ("?");
public UnaryOperatorExpression()
{
@ -63,9 +64,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -63,9 +64,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
get { return GetChildByRole (GetOperatorRole (Operator)); }
}
static Expression NoUnaryExpressionError = new ErrorExpression ("No unary expression");
public Expression Expression {
get { return GetChildByRole (Roles.Expression) ?? NoUnaryExpressionError; }
get { return GetChildByRole (Roles.Expression); }
set { SetChildByRole (Roles.Expression, value); }
}
@ -114,6 +114,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -114,6 +114,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return AddressOfRole;
case UnaryOperatorType.Await:
return AwaitRole;
case UnaryOperatorType.NullConditional:
return NullConditionalRole;
case UnaryOperatorType.NullConditionalRewrap:
return null; // no syntax
default:
throw new NotSupportedException("Invalid value for UnaryOperatorType");
}
@ -176,6 +180,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -176,6 +180,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// <summary>Get address (&a)</summary>
AddressOf,
/// <summary>C# 5.0 await</summary>
Await
Await,
/// <summary>C# 6 null-conditional operator.
/// Occurs as target of member reference or indexer expressions
/// to indicate <c>?.</c> or <c>?[]</c>.
/// Corresponds to <c>nullable.unwrap</c> in ILAst.
/// </summary>
NullConditional,
/// <summary>
/// Wrapper around a primary expression containing a null conditional operator.
/// Corresponds to <c>nullable.rewrap</c> in ILAst.
/// This has no syntax in C#, but the node is used to ensure parentheses are inserted where necessary.
/// </summary>
NullConditionalRewrap,
}
}

2
ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs

@ -186,6 +186,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -186,6 +186,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
case UnaryOperatorType.Decrement:
case UnaryOperatorType.Await:
return true;
case UnaryOperatorType.NullConditionalRewrap:
return IsValidInStatementExpression(uoe.Expression);
default:
return false;
}

7
ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

@ -201,6 +201,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -201,6 +201,13 @@ namespace ICSharpCode.Decompiler.CSharp
if (targetType.Kind == TypeKind.Unknown) {
return this; // don't attempt to insert cast to '?'
}
if (Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.NullConditional && targetType.IsReferenceType == true) {
// "(T)(x?).AccessChain" is invalid, but "((T)x)?.AccessChain" is valid and equivalent
return new UnaryOperatorExpression(
UnaryOperatorType.NullConditional,
UnwrapChild(uoe.Expression).ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion)
).WithRR(new ResolveResult(targetType)).WithoutILInstruction();
}
var compilation = expressionBuilder.compilation;
bool isLifted = type.IsKnownType(KnownTypeCode.NullableOfT) && targetType.IsKnownType(KnownTypeCode.NullableOfT);
IType utype = isLifted ? NullableType.GetUnderlyingType(type) : type;

15
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -133,6 +133,21 @@ namespace ICSharpCode.Decompiler @@ -133,6 +133,21 @@ namespace ICSharpCode.Decompiler
}
}
bool nullPropagation = true;
/// <summary>
/// Decompile C# 6 ?. and ?[] operators.
/// </summary>
public bool NullPropagation {
get { return nullPropagation; }
set {
if (nullPropagation != value) {
nullPropagation = value;
OnPropertyChanged();
}
}
}
bool automaticProperties = true;
/// <summary>

2
ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs

@ -271,7 +271,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis @@ -271,7 +271,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis
/// <summary>
/// Derived classes may add to this set of flags to ensure they don't forget to override an interesting method.
/// </summary>
protected InstructionFlags flagsRequiringManualImpl = InstructionFlags.ControlFlow | InstructionFlags.MayBranch | InstructionFlags.EndPointUnreachable;
protected InstructionFlags flagsRequiringManualImpl = InstructionFlags.ControlFlow | InstructionFlags.MayBranch | InstructionFlags.MayUnwrapNull | InstructionFlags.EndPointUnreachable;
protected sealed override void Default(ILInstruction inst)
{

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -287,6 +287,7 @@ @@ -287,6 +287,7 @@
<Compile Include="DotNetCore\UnresolvedAssemblyNameReference.cs" />
<Compile Include="IL\ILAstWritingOptions.cs" />
<Compile Include="IL\Instructions\LdFlda.cs" />
<Compile Include="IL\Instructions\NullableInstructions.cs" />
<Compile Include="IL\Instructions\StLoc.cs" />
<Compile Include="IL\SequencePoint.cs" />
<Compile Include="IL\Instructions\CallIndirect.cs" />
@ -294,6 +295,7 @@ @@ -294,6 +295,7 @@
<Compile Include="IL\Transforms\EarlyExpressionTransforms.cs" />
<Compile Include="IL\Transforms\ExpressionTreeCast.cs" />
<Compile Include="IL\Transforms\HighLevelLoopTransform.cs" />
<Compile Include="IL\Transforms\NullPropagationTransform.cs" />
<Compile Include="IL\Transforms\ProxyCallReplacer.cs" />
<Compile Include="IL\Instructions\StringToInt.cs" />
<Compile Include="IL\Instructions\UsingInstruction.cs" />

12
ICSharpCode.Decompiler/IL/InstructionFlags.cs

@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.IL @@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>
/// The instruction may read from local variables.
/// </summary>
MayReadLocals = 0x10,
MayReadLocals = 0x10,
/// <summary>
/// The instruction may write to local variables.
/// </summary>
@ -51,17 +51,21 @@ namespace ICSharpCode.Decompiler.IL @@ -51,17 +51,21 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
MayThrow = 0x100,
/// <summary>
/// The instruction may exit with a branch or return.
/// The instruction may exit with a branch or leave.
/// </summary>
MayBranch = 0x200,
/// <summary>
/// The instruction may jump to the closest containing <c>nullable.rewrap</c> instruction.
/// </summary>
MayUnwrapNull = 0x400,
/// <summary>
/// The instruction performs unconditional control flow, so that its endpoint is unreachable.
/// </summary>
/// <remarks>
/// If EndPointUnreachable is set, either MayThrow or MayBranch should also be set
/// (unless the instruction represents an infinite loop).
/// </remarks>
EndPointUnreachable = 0x400,
EndPointUnreachable = 0x800,
/// <summary>
/// The instruction contains some kind of internal control flow.
/// </summary>
@ -72,6 +76,6 @@ namespace ICSharpCode.Decompiler.IL @@ -72,6 +76,6 @@ namespace ICSharpCode.Decompiler.IL
/// Note that branch instructions don't have this flag set, because their control flow is not internal
/// (and they don't have any unusual argument evaluation rules).
/// </remarks>
ControlFlow = 0x800,
ControlFlow = 0x1000,
}
}

108
ICSharpCode.Decompiler/IL/Instructions.cs

@ -102,6 +102,13 @@ namespace ICSharpCode.Decompiler.IL @@ -102,6 +102,13 @@ namespace ICSharpCode.Decompiler.IL
ThreeValuedLogicAnd,
/// <summary>Three valued logic or. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.or(), does not have short-circuiting behavior.</summary>
ThreeValuedLogicOr,
/// <summary>The input operand must be either a nullable value type or a reference type.
/// If the input is non-null, evaluates to the (unwrapped) input.
/// If the input is null, jumps to the innermost nullable.rewrap instruction that contains this instruction.</summary>
NullableUnwrap,
/// <summary>Serves as jump target for the nullable.unwrap instruction.
/// If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null.</summary>
NullableRewrap,
/// <summary>Loads a constant string.</summary>
LdStr,
/// <summary>Loads a constant 32-bit integer.</summary>
@ -2510,6 +2517,71 @@ namespace ICSharpCode.Decompiler.IL @@ -2510,6 +2517,71 @@ namespace ICSharpCode.Decompiler.IL
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>The input operand must be either a nullable value type or a reference type.
/// If the input is non-null, evaluates to the (unwrapped) input.
/// If the input is null, jumps to the innermost nullable.rewrap instruction that contains this instruction.</summary>
public sealed partial class NullableUnwrap : UnaryInstruction
{
protected override InstructionFlags ComputeFlags()
{
return base.ComputeFlags() | InstructionFlags.MayUnwrapNull;
}
public override InstructionFlags DirectFlags {
get {
return base.DirectFlags | InstructionFlags.MayUnwrapNull;
}
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitNullableUnwrap(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitNullableUnwrap(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{
return visitor.VisitNullableUnwrap(this, context);
}
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as NullableUnwrap;
return o != null && this.Argument.PerformMatch(o.Argument, ref match);
}
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Serves as jump target for the nullable.unwrap instruction.
/// If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null.</summary>
public sealed partial class NullableRewrap : UnaryInstruction
{
public NullableRewrap(ILInstruction argument) : base(OpCode.NullableRewrap, argument)
{
}
public override void AcceptVisitor(ILVisitor visitor)
{
visitor.VisitNullableRewrap(this);
}
public override T AcceptVisitor<T>(ILVisitor<T> visitor)
{
return visitor.VisitNullableRewrap(this);
}
public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
{
return visitor.VisitNullableRewrap(this, context);
}
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as NullableRewrap;
return o != null && this.Argument.PerformMatch(o.Argument, ref match);
}
}
}
namespace ICSharpCode.Decompiler.IL
{
/// <summary>Loads a constant string.</summary>
public sealed partial class LdStr : SimpleInstruction
@ -5131,6 +5203,14 @@ namespace ICSharpCode.Decompiler.IL @@ -5131,6 +5203,14 @@ namespace ICSharpCode.Decompiler.IL
{
Default(inst);
}
protected internal virtual void VisitNullableUnwrap(NullableUnwrap inst)
{
Default(inst);
}
protected internal virtual void VisitNullableRewrap(NullableRewrap inst)
{
Default(inst);
}
protected internal virtual void VisitLdStr(LdStr inst)
{
Default(inst);
@ -5441,6 +5521,14 @@ namespace ICSharpCode.Decompiler.IL @@ -5441,6 +5521,14 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst);
}
protected internal virtual T VisitNullableUnwrap(NullableUnwrap inst)
{
return Default(inst);
}
protected internal virtual T VisitNullableRewrap(NullableRewrap inst)
{
return Default(inst);
}
protected internal virtual T VisitLdStr(LdStr inst)
{
return Default(inst);
@ -5751,6 +5839,14 @@ namespace ICSharpCode.Decompiler.IL @@ -5751,6 +5839,14 @@ namespace ICSharpCode.Decompiler.IL
{
return Default(inst, context);
}
protected internal virtual T VisitNullableUnwrap(NullableUnwrap inst, C context)
{
return Default(inst, context);
}
protected internal virtual T VisitNullableRewrap(NullableRewrap inst, C context)
{
return Default(inst, context);
}
protected internal virtual T VisitLdStr(LdStr inst, C context)
{
return Default(inst, context);
@ -5948,6 +6044,8 @@ namespace ICSharpCode.Decompiler.IL @@ -5948,6 +6044,8 @@ namespace ICSharpCode.Decompiler.IL
"addressof",
"3vl.logic.and",
"3vl.logic.or",
"nullable.unwrap",
"nullable.rewrap",
"ldstr",
"ldc.i4",
"ldc.i8",
@ -6163,6 +6261,16 @@ namespace ICSharpCode.Decompiler.IL @@ -6163,6 +6261,16 @@ namespace ICSharpCode.Decompiler.IL
right = default(ILInstruction);
return false;
}
public bool MatchNullableRewrap(out ILInstruction argument)
{
var inst = this as NullableRewrap;
if (inst != null) {
argument = inst.Argument;
return true;
}
argument = default(ILInstruction);
return false;
}
public bool MatchLdStr(out string value)
{
var inst = this as LdStr;

9
ICSharpCode.Decompiler/IL/Instructions.tt

@ -160,6 +160,15 @@ @@ -160,6 +160,15 @@
CustomClassName("ThreeValuedLogicAnd"), Binary, ResultType("O")),
new OpCode("3vl.logic.or", "Three valued logic or. Inputs are of type bool? or I4, output is of type bool?. Unlike logic.or(), does not have short-circuiting behavior.",
CustomClassName("ThreeValuedLogicOr"), Binary, ResultType("O")),
new OpCode("nullable.unwrap", "The input operand must be either a nullable value type or a reference type." + Environment.NewLine
+ "If the input is non-null, evaluates to the (unwrapped) input." + Environment.NewLine
+ "If the input is null, jumps to the innermost nullable.rewrap instruction that contains this instruction.",
Unary, CustomConstructor, CustomWriteTo, HasFlag("InstructionFlags.MayUnwrapNull")),
new OpCode("nullable.rewrap", "Serves as jump target for the nullable.unwrap instruction." + Environment.NewLine
+ "If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type)."
+ "If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,"
+ "the nullable.rewrap instruction evaluates to null.",
Unary, CustomComputeFlags),
new OpCode("ldstr", "Loads a constant string.",
CustomClassName("LdStr"), LoadConstant("string"), ResultType("O")),

9
ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs

@ -56,12 +56,17 @@ namespace ICSharpCode.Decompiler.IL @@ -56,12 +56,17 @@ namespace ICSharpCode.Decompiler.IL
{
base.CheckInvariant(phase);
Debug.Assert(condition.ResultType == StackType.I4);
Debug.Assert(trueInst.ResultType == falseInst.ResultType);
Debug.Assert(trueInst.ResultType == falseInst.ResultType
|| trueInst.HasDirectFlag(InstructionFlags.EndPointUnreachable)
|| falseInst.HasDirectFlag(InstructionFlags.EndPointUnreachable));
}
public override StackType ResultType {
get {
return trueInst.ResultType;
if (trueInst.HasDirectFlag(InstructionFlags.EndPointUnreachable))
return falseInst.ResultType;
else
return trueInst.ResultType;
}
}

79
ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
// Copyright (c) 2018 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.Diagnostics;
using System.Linq;
namespace ICSharpCode.Decompiler.IL
{
partial class NullableUnwrap
{
public NullableUnwrap(StackType unwrappedType, ILInstruction argument)
: base(OpCode.NullableUnwrap, argument)
{
this.ResultType = unwrappedType;
}
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(Argument.ResultType == StackType.O, "nullable.unwrap expects nullable type as input");
Debug.Assert(Ancestors.Any(a => a is NullableRewrap));
}
public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
{
output.Write("nullable.unwrap ");
output.Write(ResultType);
output.Write('(');
Argument.WriteTo(output, options);
output.Write(')');
}
public override StackType ResultType { get; }
}
partial class NullableRewrap
{
internal override void CheckInvariant(ILPhase phase)
{
base.CheckInvariant(phase);
Debug.Assert(Argument.HasFlag(InstructionFlags.MayUnwrapNull));
}
public override InstructionFlags DirectFlags => InstructionFlags.ControlFlow;
protected override InstructionFlags ComputeFlags()
{
// Convert MayUnwrapNull flag to ControlFlow flag.
// Also, remove EndpointUnreachable flag, because the end-point is reachable through
// the implicit nullable.unwrap branch.
const InstructionFlags flagsToRemove = InstructionFlags.MayUnwrapNull | InstructionFlags.EndPointUnreachable;
return (Argument.Flags & ~flagsToRemove) | InstructionFlags.ControlFlow;
}
public override StackType ResultType {
get {
if (Argument.ResultType == StackType.Void)
return StackType.Void;
else
return StackType.O;
}
}
}
}

8
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -292,6 +292,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -292,6 +292,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (parent is NullCoalescingInstruction && NullableType.IsNullable(v.Type)) {
return true; // inline nullables into ?? operator
}
if (parent is NullableUnwrap && NullableType.IsNullable(v.Type)) {
return true; // inline nullables into ?. operator
}
// decide based on the target into which we are inlining
switch (next.OpCode) {
case OpCode.Leave:
@ -354,6 +357,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -354,6 +357,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return FindLoadInNext(container.EntryPoint.Instructions[0], v, expressionBeingMoved, out loadInst) ?? false;
// If FindLoadInNext() returns null, we still can't continue searching
// because we can't inline over the remainder of the blockcontainer.
} else if (expr is NullableRewrap) {
// Inlining into nullable.rewrap is OK unless the expression being inlined
// contains a nullable.wrap that isn't being re-wrapped within the expression being inlined.
if (expressionBeingMoved.HasFlag(InstructionFlags.MayUnwrapNull))
return false;
}
foreach (var child in expr.Children) {
if (!child.SlotInfo.CanInlineInto)

2
ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs

@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Transform for constructing the NullCoalescingInstruction (if.notnull(a,b), or in C#: ??)
/// Note that this transform only handles the case where a,b are reference types.
///
/// The ?? operator for nullables is handled by NullableLiftingTransform.
/// The ?? operator for nullable value types is handled by NullableLiftingTransform.
/// </summary>
class NullCoalescingTransform : IStatementTransform
{

236
ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs

@ -0,0 +1,236 @@ @@ -0,0 +1,236 @@
// Copyright (c) 2018 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL.Transforms
{
/// <summary>
/// Transform that converts code patterns like "v != null ? v.M() : null" to "v?.M()"
/// </summary>
struct NullPropagationTransform
{
internal static bool IsProtectedIfInst(IfInstruction ifInst)
{
// We exclude logic.and to avoid turning
// "logic.and(comp(interfaces != ldnull), call get_Count(interfaces))"
// into "if ((interfaces?.Count ?? 0) != 0)".
return (ifInst.MatchLogicAnd(out _, out _) || ifInst.MatchLogicOr(out _, out _))
&& IfInstruction.IsInConditionSlot(ifInst);
}
readonly ILTransformContext context;
public NullPropagationTransform(ILTransformContext context)
{
this.context = context;
}
/// <summary>
/// Check if "condition ? trueInst : falseInst" can be simplified using the null-conditional operator.
/// Returns the replacement instruction, or null if no replacement is possible.
/// </summary>
internal ILInstruction Run(ILInstruction condition, ILInstruction trueInst, ILInstruction falseInst, Interval ilRange)
{
Debug.Assert(context.Settings.NullPropagation);
Debug.Assert(!condition.MatchLogicNot(out _), "Caller should pass in positive condition");
if (condition is Comp comp && comp.Left.MatchLdLoc(out var testedVar) && comp.Right.MatchLdNull()) {
if (comp.LiftingKind != ComparisonLiftingKind.None)
return null;
if (comp.Kind == ComparisonKind.Equality) {
// testedVar == null ? trueInst : falseInst
return TryNullPropagation(testedVar, falseInst, trueInst, true, ilRange);
} else if (comp.Kind == ComparisonKind.Inequality) {
return TryNullPropagation(testedVar, trueInst, falseInst, true, ilRange);
}
} else if (NullableLiftingTransform.MatchHasValueCall(condition, out testedVar)) {
// testedVar.HasValue ? trueInst : falseInst
return TryNullPropagation(testedVar, trueInst, falseInst, false, ilRange);
}
return null;
}
/// <summary>
/// testedVar != null ? nonNullInst : nullInst
/// </summary>
ILInstruction TryNullPropagation(ILVariable testedVar, ILInstruction nonNullInst, ILInstruction nullInst,
bool testedVarHasReferenceType, Interval ilRange)
{
bool removedRewrapOrNullableCtor = false;
if (NullableLiftingTransform.MatchNullableCtor(nonNullInst, out _, out var arg)) {
nonNullInst = arg;
removedRewrapOrNullableCtor = true;
} else if (nonNullInst.MatchNullableRewrap(out arg)) {
nonNullInst = arg;
removedRewrapOrNullableCtor = true;
}
if (!IsValidAccessChain(testedVar, testedVarHasReferenceType, nonNullInst, out var varLoad))
return null;
// note: InferType will be accurate in this case because the access chain consists of calls and field accesses
IType returnType = nonNullInst.InferType();
if (nullInst.MatchLdNull()) {
context.Step("Null propagation (reference type)", nonNullInst);
// testedVar != null ? testedVar.AccessChain : null
// => testedVar?.AccessChain
IntroduceUnwrap(testedVar, varLoad);
return new NullableRewrap(nonNullInst) { ILRange = ilRange };
} else if (nullInst.MatchDefaultValue(out var type) && type.IsKnownType(KnownTypeCode.NullableOfT)) {
context.Step("Null propagation (value type)", nonNullInst);
// testedVar != null ? testedVar.AccessChain : default(T?)
// => testedVar?.AccessChain
IntroduceUnwrap(testedVar, varLoad);
return new NullableRewrap(nonNullInst) { ILRange = ilRange };
} else if (!removedRewrapOrNullableCtor && NullableType.IsNonNullableValueType(returnType)) {
context.Step("Null propagation with null coalescing", nonNullInst);
// testedVar != null ? testedVar.AccessChain : nullInst
// => testedVar?.AccessChain ?? nullInst
// (only valid if AccessChain returns a non-nullable value)
IntroduceUnwrap(testedVar, varLoad);
return new NullCoalescingInstruction(
NullCoalescingKind.NullableWithValueFallback,
new NullableRewrap(nonNullInst),
nullInst
) {
UnderlyingResultType = nullInst.ResultType,
ILRange = ilRange
};
}
return null;
}
/// <summary>
/// if (x != null) x.AccessChain();
/// => x?.AccessChain();
/// </summary>
internal void RunStatements(Block block, int pos)
{
var ifInst = block.Instructions[pos] as IfInstruction;
if (ifInst == null || !ifInst.FalseInst.MatchNop())
return;
if (ifInst.Condition is Comp comp && comp.Kind == ComparisonKind.Inequality
&& comp.Left.MatchLdLoc(out var testedVar) && comp.Right.MatchLdNull()) {
TryNullPropForVoidCall(testedVar, true, ifInst.TrueInst as Block, ifInst);
} else if (NullableLiftingTransform.MatchHasValueCall(ifInst.Condition, out testedVar)) {
TryNullPropForVoidCall(testedVar, false, ifInst.TrueInst as Block, ifInst);
}
}
void TryNullPropForVoidCall(ILVariable testedVar, bool testedVarHasReferenceType, Block body, IfInstruction ifInst)
{
if (body == null || body.Instructions.Count != 1)
return;
var bodyInst = body.Instructions[0];
if (bodyInst.MatchNullableRewrap(out var arg)) {
bodyInst = arg;
}
if (!IsValidAccessChain(testedVar, testedVarHasReferenceType, bodyInst, out var varLoad))
return;
context.Step("Null-propagation (void call)", body);
// if (testedVar != null) { testedVar.AccessChain(); }
// => testedVar?.AccessChain();
IntroduceUnwrap(testedVar, varLoad);
ifInst.ReplaceWith(new NullableRewrap(
bodyInst
) { ILRange = ifInst.ILRange });
}
bool IsValidAccessChain(ILVariable testedVar, bool testedVarHasReferenceType, ILInstruction inst, out ILInstruction finalLoad)
{
finalLoad = null;
int chainLength = 0;
while (true) {
if (IsValidEndOfChain()) {
// valid end of chain
finalLoad = inst;
return chainLength >= 1;
} else if (inst.MatchLdFld(out var target, out _)) {
inst = target;
} else if (inst is CallInstruction call && call.OpCode != OpCode.NewObj) {
if (call.Arguments.Count == 0) {
return false;
}
if (call.Method.IsStatic && !call.Method.IsExtensionMethod) {
return false; // only instance or extension methods can be called with ?. syntax
}
if (call.Method.IsAccessor && !IsGetter(call.Method)) {
return false; // setter/adder/remover cannot be called with ?. syntax
}
inst = call.Arguments[0];
if (call.Method.DeclaringType.IsReferenceType == false && inst.MatchAddressOf(out var arg)) {
inst = arg;
}
// ensure the access chain does not contain any 'nullable.unwrap' that aren't directly part of the chain
for (int i = 1; i < call.Arguments.Count; ++i) {
if (call.Arguments[i].HasFlag(InstructionFlags.MayUnwrapNull)) {
return false;
}
}
} else if (inst is NullableUnwrap unwrap) {
inst = unwrap.Argument;
} else {
// unknown node -> invalid chain
return false;
}
chainLength++;
}
bool IsValidEndOfChain()
{
if (testedVarHasReferenceType) {
return inst.MatchLdLoc(testedVar);
} else {
return NullableLiftingTransform.MatchGetValueOrDefault(inst, testedVar);
}
}
}
static bool IsGetter(IMethod method)
{
return method.AccessorOwner is IProperty p && p.Getter == method;
}
private void IntroduceUnwrap(ILVariable testedVar, ILInstruction varLoad)
{
if (NullableLiftingTransform.MatchGetValueOrDefault(varLoad, testedVar)) {
varLoad.ReplaceWith(new NullableUnwrap(
varLoad.ResultType,
new LdLoc(testedVar) { ILRange = varLoad.Children[0].ILRange }
) { ILRange = varLoad.ILRange });
} else {
// Wrap varLoad in nullable.unwrap:
var children = varLoad.Parent.Children;
children[varLoad.ChildIndex] = new NullableUnwrap(testedVar.StackType, varLoad);
}
}
}
class NullPropagationStatementTransform : IStatementTransform
{
public void Run(Block block, int pos, StatementTransformContext context)
{
if (!context.Settings.NullPropagation)
return;
new NullPropagationTransform(context).RunStatements(block, pos);
}
}
}

13
ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs

@ -36,6 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -36,6 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// * lifted unary and binary operators
/// * lifted comparisons
/// * the ?? operator with type Nullable{T} on the left-hand-side
/// * the ?. operator (via NullPropagationTransform)
/// </summary>
struct NullableLiftingTransform
{
@ -55,8 +56,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -55,8 +56,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// </summary>
public bool Run(IfInstruction ifInst)
{
if (!context.Settings.LiftNullables)
return false;
var lifted = Lift(ifInst, ifInst.TrueInst, ifInst.FalseInst);
if (lifted != null) {
ifInst.ReplaceWith(lifted);
@ -67,8 +66,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -67,8 +66,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public bool RunStatements(Block block, int pos)
{
if (!context.Settings.LiftNullables)
return false;
/// e.g.:
// if (!condition) Block {
// leave IL_0000 (default.value System.Nullable`1[[System.Int64]])
@ -128,6 +125,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -128,6 +125,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
condition = arg;
ExtensionMethods.Swap(ref trueInst, ref falseInst);
}
if (context.Settings.NullPropagation && !NullPropagationTransform.IsProtectedIfInst(ifInst)) {
var nullPropagated = new NullPropagationTransform(context)
.Run(condition, trueInst, falseInst, ifInst.ILRange);
if (nullPropagated != null)
return nullPropagated;
}
if (!context.Settings.LiftNullables)
return null;
if (AnalyzeCondition(condition)) {
// (v1 != null && ... && vn != null) ? trueInst : falseInst
// => normal lifting

34
ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs

@ -171,22 +171,35 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -171,22 +171,35 @@ namespace ICSharpCode.Decompiler.IL.Transforms
int leaveIndex = entryPoint.Instructions.Count == 2 ? 1 : 2;
int checkIndex = entryPoint.Instructions.Count == 2 ? 0 : 1;
int castIndex = entryPoint.Instructions.Count == 3 ? 0 : -1;
var checkInst = entryPoint.Instructions[checkIndex];
bool isReference = objVar.Type.IsReferenceType != false;
if (castIndex > -1) {
if (!entryPoint.Instructions[castIndex].MatchStLoc(out var tempVar, out var isinst))
return false;
if (!isinst.MatchIsInst(out var load, out var disposableType) || !load.MatchLdLoc(objVar) || !disposableType.IsKnownType(KnownTypeCode.IDisposable))
return false;
if (tempVar.StoreCount != 1 || tempVar.LoadCount != 2)
if (!tempVar.IsSingleDefinition)
return false;
objVar = tempVar;
isReference = true;
if (!MatchDisposeCheck(tempVar, checkInst, isReference, usingNull, out int numObjVarLoadsInCheck))
return false;
if (tempVar.LoadCount != numObjVarLoadsInCheck)
return false;
} else {
if (!MatchDisposeCheck(objVar, checkInst, isReference, usingNull, out _))
return false;
}
if (!entryPoint.Instructions[leaveIndex].MatchLeave(container, out var returnValue) || !returnValue.MatchNop())
return false;
return true;
}
bool MatchDisposeCheck(ILVariable objVar, ILInstruction checkInst, bool isReference, bool usingNull, out int numObjVarLoadsInCheck)
{
numObjVarLoadsInCheck = 2;
CallVirt callVirt;
if (objVar.Type.IsKnownType(KnownTypeCode.NullableOfT)) {
if (!entryPoint.Instructions[checkIndex].MatchIfInstruction(out var condition, out var disposeInst))
if (!checkInst.MatchIfInstruction(out var condition, out var disposeInst))
return false;
if (!NullableLiftingTransform.MatchHasValueCall(condition, objVar))
return false;
@ -214,9 +227,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -214,9 +227,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
} else {
ILInstruction target;
if (isReference) {
if (isReference && checkInst is NullableRewrap rewrap) {
// the null check of reference types might have been transformed into "objVar?.Dispose();"
if (!(rewrap.Argument is CallVirt cv))
return false;
if (!(cv.Arguments.FirstOrDefault() is NullableUnwrap unwrap))
return false;
numObjVarLoadsInCheck = 1;
callVirt = cv;
target = unwrap.Argument;
} else if (isReference) {
// reference types have a null check.
if (!entryPoint.Instructions[checkIndex].MatchIfInstruction(out var condition, out var disposeInst))
if (!checkInst.MatchIfInstruction(out var condition, out var disposeInst))
return false;
if (!condition.MatchCompNotEquals(out var left, out var right) || !left.MatchLdLoc(objVar) || !right.MatchLdNull())
return false;
@ -231,7 +253,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -231,7 +253,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
target = newTarget;
callVirt = cv;
} else {
if (!(entryPoint.Instructions[checkIndex] is CallVirt cv))
if (!(checkInst is CallVirt cv))
return false;
target = cv.Arguments.FirstOrDefault();
if (target == null)

2
doc/copyright.txt

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
Copyright 2011-2014 for the SharpDevelop team
Copyright 2011-2018 for the SharpDevelop team
by
AlphaSierraPapa, Christoph Wille

Loading…
Cancel
Save