Browse Source

Merge branch 'master' of https://github.com/icsharpcode/ILSpy into fancy-tooltips

pull/1654/head
Siegfried Pammer 6 years ago
parent
commit
3913d78996
  1. 2
      BuildTools/appveyor-install.ps1
  2. 2
      BuildTools/pipelines-install.ps1
  3. 2
      BuildTools/update-assemblyinfo.ps1
  4. 6
      ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
  5. 1
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  6. 7
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  7. 17
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/FloatingPointArithmetic.cs
  8. 32
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/NullableTests.cs
  9. 117
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/StringConcat.cs
  10. 2
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CS1xSwitch_Debug.cs
  11. 2
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CS1xSwitch_Release.cs
  12. 2
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpUsing_Debug.cs
  13. 2
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpUsing_Release.cs
  14. 18
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs
  15. 4
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs
  16. 4
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs
  17. 8
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs
  18. 80
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs
  19. 11
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
  20. 3
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs
  21. 53
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs
  22. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs
  23. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.cs
  24. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs
  25. 33
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs
  26. 27
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.cs
  27. 93
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.opt.roslyn.il
  28. 117
      ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.roslyn.il
  29. 8
      ICSharpCode.Decompiler.Tests/UglyTestRunner.cs
  30. 1
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  31. 60
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  32. 19
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  33. 2
      ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs
  34. 37
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  35. 3
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  36. 3
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
  37. 150
      ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs
  38. 28
      ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
  39. 2
      ICSharpCode.Decompiler/DebugInfo/SequencePoint.cs
  40. 17
      ICSharpCode.Decompiler/DecompilerSettings.cs
  41. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  42. 22
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  43. 11
      ICSharpCode.Decompiler/IL/ILReader.cs
  44. 8
      ICSharpCode.Decompiler/IL/ILVariable.cs
  45. 32
      ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs
  46. 41
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  47. 57
      ICSharpCode.Decompiler/IL/Transforms/IntroduceRefReadOnlyModifierOnLocals.cs
  48. 189
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
  49. 24
      ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs
  50. 11
      ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs
  51. 12
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  52. 6
      ICSharpCode.Decompiler/TypeSystem/IMethod.cs
  53. 11
      ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs
  54. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs
  55. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs
  56. 18
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs
  57. 2
      ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs
  58. 1
      ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs
  59. 41
      ILSpy.AddIn/AssemblyFileFinder.cs
  60. 5
      ILSpy.AddIn/Commands/NuGetReferenceForILSpy.cs
  61. 13
      ILSpy.AddIn/Commands/OpenCodeItemCommand.cs
  62. 46
      ILSpy.AddIn/Commands/OpenILSpyCommand.cs
  63. 4
      ILSpy.AddIn/Commands/OpenProjectOutputCommand.cs
  64. 4
      ILSpy.AddIn/Commands/OpenReferenceCommand.cs
  65. 5
      ILSpy.AddIn/Commands/ProjectItemForILSpy.cs
  66. 5
      ILSpy.AddIn/Commands/ProjectReferenceForILSpy.cs
  67. 17
      ILSpy.AddIn/ILSpy.AddIn.csproj
  68. 6
      ILSpy.AddIn/ILSpyAddInPackage.cs
  69. 4
      ILSpy.AddIn/ILSpyInstance.cs
  70. 3
      ILSpy.AddIn/README.md
  71. 11
      ILSpy.AddIn/Utils.cs
  72. 4
      ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj
  73. 4
      ILSpy.Tests/ILSpy.Tests.csproj
  74. 117
      ILSpy/AboutPage.cs
  75. 2
      ILSpy/Analyzers/AnalyzerTreeView.cs
  76. 2
      ILSpy/Analyzers/RemoveAnalyzeContextMenuEntry.cs
  77. 4
      ILSpy/App.xaml.cs
  78. 4
      ILSpy/Commands/CheckForUpdatesCommand.cs
  79. 52
      ILSpy/Commands/DecompileInNewViewCommand.cs
  80. 16
      ILSpy/ContextMenuEntry.cs
  81. 76
      ILSpy/Controls/DockedPane.cs
  82. 2
      ILSpy/DebugSteps.xaml.cs
  83. 50
      ILSpy/Docking/DockLayoutSettings.cs
  84. 32
      ILSpy/Docking/DockingHelper.cs
  85. 9
      ILSpy/Docking/PanePosition.cs
  86. 15
      ILSpy/ILSpy.csproj
  87. 26
      ILSpy/Images/Copy.xaml
  88. 18
      ILSpy/Images/README.md
  89. 7
      ILSpy/Languages/CSharpHighlightingTokenWriter.cs
  90. 2
      ILSpy/LoadedAssembly.cs
  91. 137
      ILSpy/MainWindow.xaml
  92. 177
      ILSpy/MainWindow.xaml.cs
  93. 9
      ILSpy/Properties/Resources.Designer.cs
  94. 3
      ILSpy/Properties/Resources.resx
  95. 122
      ILSpy/Search/AbstractEntitySearchStrategy.cs
  96. 107
      ILSpy/Search/AbstractSearchStrategy.cs
  97. 111
      ILSpy/Search/AssemblySearchStrategy.cs
  98. 2
      ILSpy/Search/LiteralSearchStrategy.cs
  99. 2
      ILSpy/Search/MemberSearchStrategy.cs
  100. 2
      ILSpy/Search/MetadataTokenSearchStrategy.cs
  101. Some files were not shown because too many files have changed in this diff Show More

2
BuildTools/appveyor-install.ps1

@ -4,7 +4,7 @@ $baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463"; @@ -4,7 +4,7 @@ $baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463";
$baseCommitRev = 1;
# make sure this list matches artifacts-only branches list in appveyor.yml!
$masterBranches = @("master", "3.2.x");
$masterBranches = @("master", "5.0.x");
$globalAssemblyInfoTemplateFile = "ILSpy/Properties/AssemblyInfo.template.cs";

2
BuildTools/pipelines-install.ps1

@ -4,7 +4,7 @@ $baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463"; @@ -4,7 +4,7 @@ $baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463";
$baseCommitRev = 1;
# make sure this list matches artifacts-only branches list in azure-pipelines.yml!
$masterBranches = @("master", "3.2.x");
$masterBranches = @("master", "5.0.x");
$globalAssemblyInfoTemplateFile = "ILSpy/Properties/AssemblyInfo.template.cs";

2
BuildTools/update-assemblyinfo.ps1

@ -4,7 +4,7 @@ $baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463"; @@ -4,7 +4,7 @@ $baseCommit = "d779383cb85003d6dabeb976f0845631e07bf463";
$baseCommitRev = 1;
# make sure this list matches artifacts-only branches list in appveyor.yml!
$masterBranches = @("master", "3.2.x");
$masterBranches = @("master", "5.0.x");
$globalAssemblyInfoTemplateFile = "ILSpy/Properties/AssemblyInfo.template.cs";

6
ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs

@ -280,6 +280,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -280,6 +280,12 @@ namespace ICSharpCode.Decompiler.Tests
RunCS(options: options);
}
[Test]
public void StringConcat([ValueSource("defaultOptions")] CompilerOptions options)
{
RunCS(options: options);
}
[Test]
public void MiniJSON([ValueSource("defaultOptions")] CompilerOptions options)
{

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

@ -224,6 +224,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -224,6 +224,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
preprocessorSymbols.Add("CS71");
preprocessorSymbols.Add("CS72");
preprocessorSymbols.Add("CS73");
preprocessorSymbols.Add("CS80");
preprocessorSymbols.Add("VB11");
preprocessorSymbols.Add("VB14");
preprocessorSymbols.Add("VB15");

7
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -42,8 +42,8 @@ @@ -42,8 +42,8 @@
<ItemGroup>
<PackageReference Include="DiffLib" Version="2017.7.26.1241" />
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.3.0-beta1-final" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="3.3.0-beta1-final" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.3.1" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="3.3.1" />
<PackageReference Include="Microsoft.DiaSymReader.Converter.Xml" Version="1.1.0-beta1-63314-01" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
@ -79,9 +79,12 @@ @@ -79,9 +79,12 @@
<ItemGroup>
<Compile Include="DisassemblerPrettyTestRunner.cs" />
<Compile Include="TestCases\Correctness\StringConcat.cs" />
<Compile Include="TestCases\ILPretty\ConstantBlobs.cs" />
<Compile Include="TestCases\Pretty\OutVariables.cs" />
<Compile Include="TestCases\Pretty\CustomTaskType.cs" />
<None Include="TestCases\Ugly\NoExtensionMethods.Expected.cs" />
<Compile Include="TestCases\Ugly\NoExtensionMethods.cs" />
<None Include="TestCases\VBPretty\VBCompoundAssign.cs" />
<Compile Include="TestCases\Pretty\ThrowExpressions.cs" />
<None Include="TestCases\ILPretty\Issue1145.cs" />

17
ICSharpCode.Decompiler.Tests/TestCases/Correctness/FloatingPointArithmetic.cs

@ -7,6 +7,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -7,6 +7,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
public static int Main(string[] args)
{
Issue999();
Issue1656();
return 0;
}
@ -20,5 +21,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -20,5 +21,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
return 0.99f * v + 0.01f;
}
static void Issue1656()
{
double primary = 'B';
CxAssert((++primary) == 'C');
CxAssert((--primary) == 'B');
CxAssert((primary++) == 'B');
CxAssert((primary--) == 'C');
}
static void CxAssert(bool v)
{
if (!v) {
throw new InvalidOperationException();
}
}
}
}

32
ICSharpCode.Decompiler.Tests/TestCases/Correctness/NullableTests.cs

@ -23,6 +23,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -23,6 +23,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
class NullableTests
{
static void Main()
{
AvoidLifting();
BitNot();
}
static void AvoidLifting()
{
Console.WriteLine("MayThrow:");
Console.WriteLine(MayThrow(10, 2, 3));
@ -33,8 +39,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -33,8 +39,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
Console.WriteLine(NotUsingAllInputs(5, null));
Console.WriteLine("UsingUntestedValue:");
Console.WriteLine(NotUsingAllInputs(5, 3));
Console.WriteLine(NotUsingAllInputs(5, null));
Console.WriteLine(UsingUntestedValue(5, 3));
Console.WriteLine(UsingUntestedValue(5, null));
}
static int? MayThrow(int? a, int? b, int? c)
@ -54,5 +60,27 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -54,5 +60,27 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
// cannot be lifted because the value differs if b == null
return a.HasValue ? a.GetValueOrDefault() + b.GetValueOrDefault() : default(int?);
}
static void BitNot()
{
UInt32? value = 0;
Assert(~value == UInt32.MaxValue);
UInt64? value2 = 0;
Assert(~value2 == UInt64.MaxValue);
UInt16? value3 = 0;
Assert((UInt16)~value3 == (UInt16)UInt16.MaxValue);
UInt32 value4 = 0;
Assert(~value4 == UInt32.MaxValue);
UInt64 value5 = 0;
Assert(~value5 == UInt64.MaxValue);
UInt16 value6 = 0;
Assert((UInt16)~value6 == UInt16.MaxValue);
}
static void Assert(bool b)
{
if (!b)
throw new InvalidOperationException();
}
}
}

117
ICSharpCode.Decompiler.Tests/TestCases/Correctness/StringConcat.cs

@ -0,0 +1,117 @@ @@ -0,0 +1,117 @@
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
{
class StringConcat
{
private class C
{
int i;
public C(int i)
{
Console.WriteLine(" new C(" + i + ")");
this.i = i;
}
public override string ToString()
{
Console.WriteLine(" C(" + i + ").ToString()");
return (i++).ToString();
}
}
private struct S
{
int i;
public S(int i)
{
Console.WriteLine(" new C(" + i + ")");
this.i = i;
}
public override string ToString()
{
Console.WriteLine(" S(" + i + ").ToString()");
return (i++).ToString();
}
}
static string Space()
{
Console.WriteLine(" Space()");
return " ";
}
static void TestClass()
{
Console.WriteLine("string + C:");
Console.WriteLine(Space() + new C(1));
Console.WriteLine("C + string:");
Console.WriteLine(new C(2) + Space());
Console.WriteLine("C + string + C:");
Console.WriteLine(new C(3) + Space() + new C(4));
Console.WriteLine("string + C + C:");
Console.WriteLine(Space() + new C(5) + new C(6));
Console.WriteLine("string.Concat(C, string, C):");
Console.WriteLine(string.Concat(new C(10), Space(), new C(11)));
Console.WriteLine("string.Concat(string.Concat(C, string), C):");
Console.WriteLine(string.Concat(string.Concat(new C(15), Space()), new C(16)));
Console.WriteLine("string.Concat(C, string.Concat(string, C)):");
Console.WriteLine(string.Concat(new C(20), string.Concat(Space(), new C(21))));
Console.WriteLine("string.Concat(C, string) + C:");
Console.WriteLine(string.Concat(new C(30), Space()) + new C(31));
}
static void TestStruct()
{
Console.WriteLine("string + S:");
Console.WriteLine(Space() + new S(1));
Console.WriteLine("S + string:");
Console.WriteLine(new S(2) + Space());
Console.WriteLine("S + string + S:");
Console.WriteLine(new S(3) + Space() + new S(4));
Console.WriteLine("string + S + S:");
Console.WriteLine(Space() + new S(5) + new S(6));
Console.WriteLine("string.Concat(S, string, S):");
Console.WriteLine(string.Concat(new S(10), Space(), new S(11)));
Console.WriteLine("string.Concat(string.Concat(S, string), S):");
Console.WriteLine(string.Concat(string.Concat(new S(15), Space()), new S(16)));
Console.WriteLine("string.Concat(S, string.Concat(string, S)):");
Console.WriteLine(string.Concat(new S(20), string.Concat(Space(), new S(21))));
Console.WriteLine("string.Concat(S, string) + S:");
Console.WriteLine(string.Concat(new S(30), Space()) + new S(31));
}
static void TestStructMutation()
{
Console.WriteLine("TestStructMutation:");
S s = new S(0);
Console.WriteLine(Space() + s);
Console.WriteLine(Space() + s.ToString());
Console.WriteLine(s);
}
static void Main()
{
TestClass();
TestStruct();
TestStructMutation();
}
}
}

2
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CS1xSwitch_Debug.cs

@ -270,7 +270,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -270,7 +270,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static string SwitchOverBool(bool b)
{
Console.WriteLine("SwitchOverBool: " + b.ToString());
Console.WriteLine("SwitchOverBool: " + b);
switch (b) {
case true:
return bool.TrueString;

2
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/CS1xSwitch_Release.cs

@ -270,7 +270,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -270,7 +270,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static string SwitchOverBool(bool b)
{
Console.WriteLine("SwitchOverBool: " + b.ToString());
Console.WriteLine("SwitchOverBool: " + b);
switch (b) {
case true:
return bool.TrueString;

2
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpUsing_Debug.cs

@ -36,7 +36,7 @@ public static class FSharpUsingPatterns @@ -36,7 +36,7 @@ public static class FSharpUsingPatterns
num = fileStream.ReadByte();
}
int num2 = num;
Console.WriteLine("read:" + num2.ToString());
Console.WriteLine("read:" + num2);
}
public static void sample5()

2
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/FSharpUsing_Release.cs

@ -36,7 +36,7 @@ public static class FSharpUsingPatterns @@ -36,7 +36,7 @@ public static class FSharpUsingPatterns
num = fileStream.ReadByte();
}
int num2 = num;
Console.WriteLine("read:" + num2.ToString());
Console.WriteLine("read:" + num2);
}
public static void sample5()

18
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs

@ -167,6 +167,24 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -167,6 +167,24 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
};
}
public static async Task AlwaysThrow()
{
throw null;
}
public static async Task InfiniteLoop()
{
while (true) {
}
}
public static async Task InfiniteLoopWithAwait()
{
while (true) {
await Task.Delay(10);
}
}
#if CS70
public static async Task<int> AsyncLocalFunctions()
{

4
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs

@ -4562,9 +4562,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -4562,9 +4562,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
// same temporary. In order to inline the generated value-type temporary, we
// need to split it, even though it has the address taken for the ToString() call.
if (flag) {
strings[1] += chars[i].ToString();
strings[1] += chars[i];
} else {
strings[0] += chars[i].ToString();
strings[0] += chars[i];
}
}
#endif

4
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CustomTaskType.cs

@ -1,4 +1,6 @@ @@ -1,4 +1,6 @@
using System;
#pragma warning disable 1998
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

8
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs

@ -1026,6 +1026,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1026,6 +1026,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
});
}
public static void StringConcat()
{
Test<Func<string, object, string>>(null, (string a, object b) => a + b);
Test<Func<string, object, string>>(null, (string a, object b) => a + b.ToString());
Test<Func<string, int, string>>(null, (string a, int b) => a + b);
Test<Func<string, int, string>>(null, (string a, int b) => a + b.ToString());
}
public async Task Issue1524(string str)
{
await Task.Delay(100);

80
ICSharpCode.Decompiler.Tests/TestCases/Pretty/InterfaceTests.cs

@ -16,21 +16,101 @@ @@ -16,21 +16,101 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
// We can't test this because "error CS8701: Target runtime doesn't support default interface implementation."
#undef CS80
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal class InterfaceTests
{
public interface IA
{
int Property1 {
get;
}
int Property2 {
set;
}
int Property3 {
get;
set;
}
event EventHandler MyEvent;
void Method();
#if CS80
void DefaultMethod()
{
Method();
PrivateMethod();
}
private void PrivateMethod()
{
Method();
}
internal void InternalMethod()
{
Method();
}
sealed void SealedMethod()
{
Method();
}
static void StaticMethod()
{
}
#endif
}
public interface IA2 : IA
{
#if CS80
void IA.InternalMethod()
{
}
#endif
}
public interface IB
{
}
public class C : IA2, IA, IB
{
int IA.Property1 {
get {
throw new NotImplementedException();
}
}
int IA.Property2 {
set {
throw new NotImplementedException();
}
}
int IA.Property3 {
get {
throw new NotImplementedException();
}
set {
throw new NotImplementedException();
}
}
event EventHandler IA.MyEvent {
add {
}
remove {
}
}
void IA.Method()
{
throw new NotImplementedException();
}
}
}
}

11
ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs

@ -310,6 +310,17 @@ namespace LocalFunctions @@ -310,6 +310,17 @@ namespace LocalFunctions
}
}
public void WriteCapturedParameter(int i)
{
ParamWrite();
Console.WriteLine(i);
void ParamWrite()
{
i++;
}
}
//public static void LocalFunctionInUsing()
//{
// using (MemoryStream memoryStream = new MemoryStream()) {

3
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs

@ -401,7 +401,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -401,7 +401,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine("MoveNext");
if (enumerator.MoveNext()) {
object current = enumerator.Current;
Console.WriteLine("current: " + current);
Console.WriteLine("please don't inline 'current'");
Console.WriteLine(current);
}
} finally {
IDisposable disposable = enumerator as IDisposable;

53
ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs

@ -63,17 +63,68 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -63,17 +63,68 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
private readonly int dummy;
public int Property {
get {
return 1;
}
set {
}
}
#if CS80
public readonly int ReadOnlyProperty {
get {
return 1;
}
set {
}
}
public int PropertyWithReadOnlyGetter {
readonly get {
return 1;
}
set {
}
}
public int PropertyWithReadOnlySetter {
get {
return 1;
}
readonly set {
}
}
public event EventHandler NormalEvent;
public readonly event EventHandler ReadOnlyEvent {
add {
}
remove {
}
}
#endif
public void Method()
{
}
#if CS80
public readonly void ReadOnlyMethod()
{
}
#endif
}
public readonly struct ReadOnlyStruct
{
private readonly int dummy;
private readonly int Field;
public void Method()
{
ref readonly int field = ref Field;
Console.WriteLine("No inlining");
Console.WriteLine(field.GetHashCode());
}
}

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs

@ -366,7 +366,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -366,7 +366,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#if !ROSLYN
public static string SwitchOverBool(bool b)
{
Console.WriteLine("SwitchOverBool: " + b.ToString());
Console.WriteLine("SwitchOverBool: " + b);
switch (b) {
case true:
return bool.TrueString;

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Using.cs

@ -90,7 +90,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -90,7 +90,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
private void UsingStatementOnNullableStruct(UsingStruct? us)
{
using (us) {
Console.WriteLine("using-body: " + us);
Console.WriteLine("using-body: " + us.ToString());
}
}

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs

@ -184,7 +184,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -184,7 +184,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public static void Issue56(int i, out string str)
{
str = "qq";
str += i.ToString();
str += i;
}
public static void CopyAroundAndModifyField(S s)

33
ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.Expected.cs

@ -0,0 +1,33 @@ @@ -0,0 +1,33 @@
using System;
using System.Runtime.CompilerServices;
[assembly: Extension]
namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly
{
[Extension]
internal static class NoExtensionMethods
{
[Extension]
internal static Func<T> AsFunc<T>(T value) where T : class
{
return new Func<T>(value, __ldftn(Return));
}
[Extension]
private static T Return<T>(T value)
{
return value;
}
internal static Func<int, int> ExtensionMethodAsStaticFunc()
{
return Return;
}
internal static Func<object> ExtensionMethodBoundToNull()
{
return new Func<object>(null, __ldftn(Return));
}
}
}

27
ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.cs

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Ugly
{
internal static class NoExtensionMethods
{
internal static Func<T> AsFunc<T>(this T value) where T : class
{
return new Func<T>(value.Return);
}
private static T Return<T>(this T value)
{
return value;
}
internal static Func<int, int> ExtensionMethodAsStaticFunc()
{
return Return;
}
internal static Func<object> ExtensionMethodBoundToNull()
{
return ((object)null).Return;
}
}
}

93
ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.opt.roslyn.il

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
// 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 NoExtensionMethods
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )
.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 NoExtensionMethods.dll
.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
// =============== CLASS MEMBERS DECLARATION ===================
.class private abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )
.method assembly hidebysig static class [mscorlib]System.Func`1<!!T>
AsFunc<class T>(!!T 'value') cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )
// Code size 18 (0x12)
.maxstack 8
IL_0000: ldarg.0
IL_0001: box !!T
IL_0006: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return<!!0>(!!0)
IL_000c: newobj instance void class [mscorlib]System.Func`1<!!T>::.ctor(object,
native int)
IL_0011: ret
} // end of method NoExtensionMethods::AsFunc
.method private hidebysig static !!T Return<T>(!!T 'value') cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )
// Code size 2 (0x2)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ret
} // end of method NoExtensionMethods::Return
.method assembly hidebysig static class [mscorlib]System.Func`2<int32,int32>
ExtensionMethodAsStaticFunc() cil managed
{
// Code size 13 (0xd)
.maxstack 8
IL_0000: ldnull
IL_0001: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return<int32>(!!0)
IL_0007: newobj instance void class [mscorlib]System.Func`2<int32,int32>::.ctor(object,
native int)
IL_000c: ret
} // end of method NoExtensionMethods::ExtensionMethodAsStaticFunc
.method assembly hidebysig static class [mscorlib]System.Func`1<object>
ExtensionMethodBoundToNull() cil managed
{
// Code size 13 (0xd)
.maxstack 8
IL_0000: ldnull
IL_0001: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return<object>(!!0)
IL_0007: newobj instance void class [mscorlib]System.Func`1<object>::.ctor(object,
native int)
IL_000c: ret
} // end of method NoExtensionMethods::ExtensionMethodBoundToNull
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************

117
ICSharpCode.Decompiler.Tests/TestCases/Ugly/NoExtensionMethods.roslyn.il

@ -0,0 +1,117 @@ @@ -0,0 +1,117 @@
// 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 NoExtensionMethods
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )
.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 07 01 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 NoExtensionMethods.dll
.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
// =============== CLASS MEMBERS DECLARATION ===================
.class private abstract auto ansi sealed beforefieldinit ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )
.method assembly hidebysig static class [mscorlib]System.Func`1<!!T>
AsFunc<class T>(!!T 'value') cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )
// Code size 23 (0x17)
.maxstack 2
.locals init (class [mscorlib]System.Func`1<!!T> V_0)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: box !!T
IL_0007: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return<!!0>(!!0)
IL_000d: newobj instance void class [mscorlib]System.Func`1<!!T>::.ctor(object,
native int)
IL_0012: stloc.0
IL_0013: br.s IL_0015
IL_0015: ldloc.0
IL_0016: ret
} // end of method NoExtensionMethods::AsFunc
.method private hidebysig static !!T Return<T>(!!T 'value') cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 )
// Code size 7 (0x7)
.maxstack 1
.locals init (!!T V_0)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.0
IL_0003: br.s IL_0005
IL_0005: ldloc.0
IL_0006: ret
} // end of method NoExtensionMethods::Return
.method assembly hidebysig static class [mscorlib]System.Func`2<int32,int32>
ExtensionMethodAsStaticFunc() cil managed
{
// Code size 18 (0x12)
.maxstack 2
.locals init (class [mscorlib]System.Func`2<int32,int32> V_0)
IL_0000: nop
IL_0001: ldnull
IL_0002: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return<int32>(!!0)
IL_0008: newobj instance void class [mscorlib]System.Func`2<int32,int32>::.ctor(object,
native int)
IL_000d: stloc.0
IL_000e: br.s IL_0010
IL_0010: ldloc.0
IL_0011: ret
} // end of method NoExtensionMethods::ExtensionMethodAsStaticFunc
.method assembly hidebysig static class [mscorlib]System.Func`1<object>
ExtensionMethodBoundToNull() cil managed
{
// Code size 18 (0x12)
.maxstack 2
.locals init (class [mscorlib]System.Func`1<object> V_0)
IL_0000: nop
IL_0001: ldnull
IL_0002: ldftn !!0 ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods::Return<object>(!!0)
IL_0008: newobj instance void class [mscorlib]System.Func`1<object>::.ctor(object,
native int)
IL_000d: stloc.0
IL_000e: br.s IL_0010
IL_0010: ldloc.0
IL_0011: ret
} // end of method NoExtensionMethods::ExtensionMethodBoundToNull
} // end of class ICSharpCode.Decompiler.Tests.TestCases.Ugly.NoExtensionMethods
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************

8
ICSharpCode.Decompiler.Tests/UglyTestRunner.cs

@ -83,6 +83,14 @@ namespace ICSharpCode.Decompiler.Tests @@ -83,6 +83,14 @@ namespace ICSharpCode.Decompiler.Tests
});
}
[Test]
public void NoExtensionMethods([ValueSource("roslynOnlyOptions")] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings {
ExtensionMethods = false
});
}
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);

1
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -105,6 +105,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -105,6 +105,7 @@ namespace ICSharpCode.Decompiler.CSharp
new SwitchOnStringTransform(),
new SwitchOnNullableTransform(),
new SplitVariables(), // split variables once again, because SwitchOnNullableTransform eliminates ldloca
new IntroduceRefReadOnlyModifierOnLocals(),
new BlockILTransform { // per-block transforms
PostOrderTransforms = {
// Even though it's a post-order block-transform as most other transforms,

60
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -1235,7 +1235,38 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1235,7 +1235,38 @@ namespace ICSharpCode.Decompiler.CSharp
default:
throw new ArgumentException($"Unknown instruction type: {func.OpCode}");
}
return HandleDelegateConstruction(inst.Method.DeclaringType, method, expectedTargetDetails, thisArg, inst);
if (CanUseDelegateConstruction(method, thisArg, inst.Method.DeclaringType.GetDelegateInvokeMethod())) {
return HandleDelegateConstruction(inst.Method.DeclaringType, method, expectedTargetDetails, thisArg, inst);
} else {
var argumentList = BuildArgumentList(expectedTargetDetails, null, inst.Method,
0, inst.Arguments, null);
return HandleConstructorCall(new ExpectedTargetDetails { CallOpCode = OpCode.NewObj }, null, inst.Method, argumentList).WithILInstruction(inst);
}
}
private bool CanUseDelegateConstruction(IMethod targetMethod, ILInstruction thisArg, IMethod invokeMethod)
{
if (targetMethod.IsStatic) {
// If the invoke method is known, we can compare the parameter counts to figure out whether the
// delegate is static or binds the first argument
if (invokeMethod != null) {
if (invokeMethod.Parameters.Count == targetMethod.Parameters.Count) {
return thisArg.MatchLdNull();
} else if (targetMethod.IsExtensionMethod && invokeMethod.Parameters.Count == targetMethod.Parameters.Count - 1) {
return true;
} else {
return false;
}
} else {
// delegate type unknown:
return thisArg.MatchLdNull() || targetMethod.IsExtensionMethod;
}
} else {
// targetMethod is instance method
if (invokeMethod != null && invokeMethod.Parameters.Count != targetMethod.Parameters.Count)
return false;
return true;
}
}
internal TranslatedExpression Build(LdVirtDelegate inst)
@ -1243,9 +1274,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1243,9 +1274,15 @@ namespace ICSharpCode.Decompiler.CSharp
return HandleDelegateConstruction(inst.Type, inst.Method, new ExpectedTargetDetails { CallOpCode = OpCode.CallVirt }, inst.Argument, inst);
}
TranslatedExpression HandleDelegateConstruction(IType delegateType, IMethod method, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg, ILInstruction inst)
internal ExpressionWithResolveResult BuildMethodReference(IMethod method, bool isVirtual)
{
var expr = BuildDelegateReference(method, invokeMethod: null, new ExpectedTargetDetails { CallOpCode = isVirtual ? OpCode.CallVirt : OpCode.Call }, thisArg: null);
expr.Expression.RemoveAnnotations<ResolveResult>();
return expr.Expression.WithRR(new MemberResolveResult(null, method));
}
ExpressionWithResolveResult BuildDelegateReference(IMethod method, IMethod invokeMethod, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg)
{
var invokeMethod = delegateType.GetDelegateInvokeMethod();
TranslatedExpression target;
IType targetType;
bool requireTarget;
@ -1331,27 +1368,32 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1331,27 +1368,32 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
requireTarget = !method.IsLocalFunction && (step & 1) != 0;
Expression targetExpression;
ExpressionWithResolveResult targetExpression;
Debug.Assert(result != null);
if (requireTarget) {
Debug.Assert(target.Expression != null);
var mre = new MemberReferenceExpression(target, methodName);
if ((step & 2) != 0)
mre.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
mre.WithRR(result);
targetExpression = mre;
targetExpression = mre.WithRR(result);
} else {
var ide = new IdentifierExpression(methodName);
if ((step & 2) != 0)
ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
ide.WithRR(result);
targetExpression = ide;
targetExpression = ide.WithRR(result);
}
return targetExpression;
}
TranslatedExpression HandleDelegateConstruction(IType delegateType, IMethod method, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg, ILInstruction inst)
{
var invokeMethod = delegateType.GetDelegateInvokeMethod();
var targetExpression = BuildDelegateReference(method, invokeMethod, expectedTargetDetails, thisArg);
var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(delegateType), targetExpression)
.WithILInstruction(inst)
.WithRR(new ConversionResolveResult(
delegateType,
result,
targetExpression.ResolveResult,
Conversion.MethodGroupConversion(method, expectedTargetDetails.CallOpCode == OpCode.CallVirt, false)));
return oce;
}

19
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1409,7 +1409,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1409,7 +1409,8 @@ namespace ICSharpCode.Decompiler.CSharp
if (UserDefinedCompoundAssign.IsStringConcat(inst.Method)) {
Debug.Assert(inst.Method.Parameters.Count == 2);
var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this, allowImplicitConversion: true);
return new AssignmentExpression(target, AssignmentOperatorType.Add, value)
var valueExpr = ReplaceMethodCallsWithOperators.RemoveRedundantToStringInConcat(value, inst.Method, isLastArgument: true).Detach();
return new AssignmentExpression(target, AssignmentOperatorType.Add, valueExpr)
.WithILInstruction(inst)
.WithRR(new OperatorResolveResult(inst.Method.ReturnType, ExpressionType.AddAssign, inst.Method, inst.IsLifted, new[] { target.ResolveResult, value.ResolveResult }));
} else if (inst.Method.Parameters.Count == 2) {
@ -3134,6 +3135,22 @@ namespace ICSharpCode.Decompiler.CSharp @@ -3134,6 +3135,22 @@ namespace ICSharpCode.Decompiler.CSharp
.WithRR(new OperatorResolveResult(SpecialType.Dynamic, inst.Operation, new[] { target.ResolveResult, value.ResolveResult }));
}
protected internal override TranslatedExpression VisitLdFtn(LdFtn inst, TranslationContext context)
{
ExpressionWithResolveResult delegateRef = new CallBuilder(this, typeSystem, settings).BuildMethodReference(inst.Method, isVirtual: false);
return new InvocationExpression(new IdentifierExpression("__ldftn"), delegateRef)
.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.IntPtr)))
.WithILInstruction(inst);
}
protected internal override TranslatedExpression VisitLdVirtFtn(LdVirtFtn inst, TranslationContext context)
{
ExpressionWithResolveResult delegateRef = new CallBuilder(this, typeSystem, settings).BuildMethodReference(inst.Method, isVirtual: true);
return new InvocationExpression(new IdentifierExpression("__ldvirtftn"), delegateRef)
.WithRR(new ResolveResult(compilation.FindType(KnownTypeCode.IntPtr)))
.WithILInstruction(inst);
}
protected internal override TranslatedExpression VisitInvalidBranch(InvalidBranch inst, TranslationContext context)
{
string message = "Error";

2
ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs

@ -300,7 +300,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -300,7 +300,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (sequencePoint.Offset < pos) {
// overlapping sequence point?
// delete previous sequence points that are after sequencePoint.Offset
while (newList.Count > 0 && newList.Last().EndOffset > pos) {
while (newList.Count > 0 && newList.Last().EndOffset > sequencePoint.Offset) {
var last = newList.Last();
if (last.Offset >= sequencePoint.Offset) {
newList.RemoveAt(newList.Count - 1);

37
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -1522,6 +1522,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1522,6 +1522,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
Accessor decl = new Accessor();
if (this.ShowAccessibility && accessor.Accessibility != ownerAccessibility)
decl.Modifiers = ModifierFromAccessibility(accessor.Accessibility);
if (accessor.ThisIsRefReadOnly && accessor.DeclaringTypeDefinition?.IsReadOnly == false)
decl.Modifiers |= Modifiers.Readonly;
if (ShowAttributes) {
decl.Attributes.AddRange(ConvertAttributes(accessor.GetAttributes()));
decl.Attributes.AddRange(ConvertAttributes(accessor.GetReturnTypeAttributes(), "return"));
@ -1551,9 +1553,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1551,9 +1553,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
decl.Getter = ConvertAccessor(property.Getter, property.Accessibility, false);
decl.Setter = ConvertAccessor(property.Setter, property.Accessibility, true);
decl.PrivateImplementationType = GetExplicitInterfaceType (property);
MergeReadOnlyModifiers(decl, decl.Getter, decl.Setter);
return decl;
}
static void MergeReadOnlyModifiers(EntityDeclaration decl, Accessor accessor1, Accessor accessor2)
{
if (accessor1.HasModifier(Modifiers.Readonly) && accessor2.HasModifier(Modifiers.Readonly)) {
accessor1.Modifiers &= ~Modifiers.Readonly;
accessor2.Modifiers &= ~Modifiers.Readonly;
decl.Modifiers |= Modifiers.Readonly;
}
}
IndexerDeclaration ConvertIndexer(IProperty indexer)
{
IndexerDeclaration decl = new IndexerDeclaration();
@ -1571,6 +1582,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1571,6 +1582,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
decl.Getter = ConvertAccessor(indexer.Getter, indexer.Accessibility, false);
decl.Setter = ConvertAccessor(indexer.Setter, indexer.Accessibility, true);
decl.PrivateImplementationType = GetExplicitInterfaceType (indexer);
MergeReadOnlyModifiers(decl, decl.Getter, decl.Setter);
return decl;
}
@ -1590,6 +1602,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1590,6 +1602,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
decl.AddAccessor = ConvertAccessor(ev.AddAccessor, ev.Accessibility, true);
decl.RemoveAccessor = ConvertAccessor(ev.RemoveAccessor, ev.Accessibility, true);
decl.PrivateImplementationType = GetExplicitInterfaceType (ev);
MergeReadOnlyModifiers(decl, decl.AddAccessor, decl.RemoveAccessor);
return decl;
} else {
EventDeclaration decl = new EventDeclaration();
@ -1728,8 +1741,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1728,8 +1741,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
bool NeedsAccessibility(IMember member)
{
var declaringType = member.DeclaringType;
if ((declaringType != null && declaringType.Kind == TypeKind.Interface) || member.IsExplicitInterfaceImplementation)
if (member.IsExplicitInterfaceImplementation)
return false;
if (declaringType != null && declaringType.Kind == TypeKind.Interface) {
return member.Accessibility != Accessibility.Public;
}
switch (member.SymbolKind) {
case SymbolKind.Constructor:
return !member.IsStatic;
@ -1751,14 +1767,21 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1751,14 +1767,21 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
m |= Modifiers.Static;
} else {
var declaringType = member.DeclaringType;
if (member.IsAbstract && declaringType != null && declaringType.Kind != TypeKind.Interface)
m |= Modifiers.Abstract;
if (member.IsOverride)
if (declaringType.Kind == TypeKind.Interface) {
if (!member.IsVirtual && !member.IsAbstract && !member.IsOverride && member.Accessibility != Accessibility.Private)
m |= Modifiers.Sealed;
} else {
if (member.IsAbstract)
m |= Modifiers.Abstract;
else if (member.IsVirtual && !member.IsOverride)
m |= Modifiers.Virtual;
}
if (member.IsOverride && !member.IsExplicitInterfaceImplementation)
m |= Modifiers.Override;
if (member.IsVirtual && !member.IsAbstract && !member.IsOverride && declaringType.Kind != TypeKind.Interface)
m |= Modifiers.Virtual;
if (member.IsSealed)
if (member.IsSealed && !member.IsExplicitInterfaceImplementation)
m |= Modifiers.Sealed;
if (member is IMethod method && method.ThisIsRefReadOnly && method.DeclaringTypeDefinition?.IsReadOnly == false)
m |= Modifiers.Readonly;
}
}
return m;

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

@ -447,6 +447,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -447,6 +447,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} else {
type = context.TypeSystemAstBuilder.ConvertType(v.Type);
}
if (v.ILVariable.IsRefReadOnly && type is ComposedType composedType && composedType.HasRefSpecifier) {
composedType.HasReadOnlySpecifier = true;
}
var vds = new VariableDeclarationStatement(type, v.Name, assignment.Right.Detach());
var init = vds.Variables.Single();
init.AddAnnotation(assignment.Left.GetResolveResult());

3
ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

@ -571,11 +571,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -571,11 +571,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
if (field == null)
return null;
if (propertyDeclaration.Setter.HasModifier(Modifiers.Readonly))
return null;
if (field.IsCompilerGenerated() && field.DeclaringTypeDefinition == property.DeclaringTypeDefinition) {
RemoveCompilerGeneratedAttribute(propertyDeclaration.Getter.Attributes);
RemoveCompilerGeneratedAttribute(propertyDeclaration.Setter.Attributes);
propertyDeclaration.Getter.Body = null;
propertyDeclaration.Setter.Body = null;
propertyDeclaration.Getter.Modifiers &= ~Modifiers.Readonly;
// Add C# 7.3 attributes on backing field:
var attributes = field.GetAttributes()

150
ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs

@ -23,7 +23,9 @@ using System.Reflection; @@ -23,7 +23,9 @@ using System.Reflection;
using System.Text;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.CSharp.Transforms
{
@ -56,11 +58,19 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -56,11 +58,19 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
var arguments = invocationExpression.Arguments.ToArray();
// Reduce "String.Concat(a, b)" to "a + b"
if (method.Name == "Concat" && method.DeclaringType.FullName == "System.String" && CheckArgumentsForStringConcat(arguments)) {
invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression
Expression expr = arguments[0];
if (IsStringConcat(method) && CheckArgumentsForStringConcat(arguments)) {
bool isInExpressionTree = invocationExpression.Ancestors.OfType<LambdaExpression>().Any(
lambda => lambda.Annotation<IL.ILFunction>()?.Kind == IL.ILFunctionKind.ExpressionTree);
Expression expr = arguments[0].Detach();
if (!isInExpressionTree) {
expr = RemoveRedundantToStringInConcat(expr, method, isLastArgument: false).Detach();
}
for (int i = 1; i < arguments.Length; i++) {
expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Add, arguments[i].UnwrapInDirectionExpression());
var arg = arguments[i].Detach();
if (!isInExpressionTree) {
arg = RemoveRedundantToStringInConcat(arg, method, isLastArgument: i == arguments.Length - 1).Detach();
}
expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Add, arg);
}
expr.CopyAnnotationsFrom(invocationExpression);
invocationExpression.ReplaceWith(expr);
@ -174,9 +184,135 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -174,9 +184,135 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (arguments.Length < 2)
return false;
return !arguments.Any(arg => arg is NamedArgumentExpression) &&
(arguments[0].GetResolveResult().Type.IsKnownType(KnownTypeCode.String) ||
arguments[1].GetResolveResult().Type.IsKnownType(KnownTypeCode.String));
if (arguments.Any(arg => arg is NamedArgumentExpression))
return false;
// The evaluation order when the object.ToString() calls happen is a mess:
// The C# spec says the evaluation for order for each individual string + should be:
// * evaluate left argument
// * evaluate right argument
// * call ToString() on object argument
// What actually happens pre-VS2019.3:
// * evaluate all arguments in chain of + operators from left to right
// * call ToString() on all object arguments from left to right
// What happens in VS2019.3:
// * for each argument in chain of + operators fom left to right:
// * evaluate argument
// * call ToString() on object argument
// See https://github.com/dotnet/roslyn/issues/38641 for details.
// To ensure the decompiled code's behavior matches the original IL behavior,
// no matter which compiler is used to recompile it, we require that all
// implicit ToString() calls except for the last are free of side effects.
foreach (var arg in arguments.SkipLast(1)) {
if (!ToStringIsKnownEffectFree(arg.GetResolveResult().Type)) {
return false;
}
}
foreach (var arg in arguments) {
if (arg.GetResolveResult() is InvocationResolveResult rr && IsStringConcat(rr.Member)) {
// Roslyn + mcs also flatten nested string.Concat() invocations within a operator+ use,
// which causes it to use the incorrect evaluation order despite the code using an
// explicit string.Concat() call.
// This problem is avoided if the outer call remains string.Concat() as well.
return false;
}
}
// One of the first two arguments must be string, otherwise the + operator
// won't resolve to a string concatenation.
return arguments[0].GetResolveResult().Type.IsKnownType(KnownTypeCode.String)
|| arguments[1].GetResolveResult().Type.IsKnownType(KnownTypeCode.String);
}
private bool IsStringConcat(IParameterizedMember member)
{
return member is IMethod method
&& method.Name == "Concat"
&& method.DeclaringType.IsKnownType(KnownTypeCode.String);
}
static readonly Pattern ToStringCallPattern = new Choice {
// target.ToString()
new InvocationExpression(new MemberReferenceExpression(new AnyNode("target"), "ToString")).WithName("call"),
// target?.ToString()
new UnaryOperatorExpression(
UnaryOperatorType.NullConditionalRewrap,
new InvocationExpression(
new MemberReferenceExpression(
new UnaryOperatorExpression(UnaryOperatorType.NullConditional, new AnyNode("target")),
"ToString")
).WithName("call")
).WithName("nullConditional")
};
internal static Expression RemoveRedundantToStringInConcat(Expression expr, IMethod concatMethod, bool isLastArgument)
{
var m = ToStringCallPattern.Match(expr);
if (!m.Success)
return expr;
if (!concatMethod.Parameters.All(IsStringParameter)) {
// If we're using a string.Concat() overload involving object parameters,
// string.Concat() itself already calls ToString() so the C# compiler shouldn't
// generate additional ToString() calls in this case.
return expr;
}
var toStringMethod = m.Get<Expression>("call").Single().GetSymbol() as IMethod;
var target = m.Get<Expression>("target").Single();
var type = target.GetResolveResult().Type;
if (!(isLastArgument || ToStringIsKnownEffectFree(type))) {
// ToString() order of evaluation matters, see CheckArgumentsForStringConcat().
return expr;
}
if (type.IsReferenceType != false && !m.Has("nullConditional")) {
// ToString() might throw NullReferenceException, but the builtin operator+ doesn't.
return expr;
}
if (!ToStringIsKnownEffectFree(type) && toStringMethod != null && IL.Transforms.ILInlining.MethodRequiresCopyForReadonlyLValue(toStringMethod)) {
// ToString() on a struct may mutate the struct.
// For operator+ the C# compiler creates a temporary copy before implicitly calling ToString(),
// whereas an explicit ToString() call would mutate the original lvalue.
// So we can't remove the compiler-generated ToString() call in cases where this might make a difference.
return expr;
}
// All checks succeeded, we can eliminate the ToString() call.
// The C# compiler will generate an equivalent call if the code is recompiled.
return target;
bool IsStringParameter(IParameter p)
{
IType ty = p.Type;
if (p.IsParams && ty.Kind == TypeKind.Array)
ty = ((ArrayType)ty).ElementType;
return ty.IsKnownType(KnownTypeCode.String);
}
}
static bool ToStringIsKnownEffectFree(IType type)
{
type = NullableType.GetUnderlyingType(type);
switch (type.GetDefinition()?.KnownTypeCode) {
case KnownTypeCode.Boolean:
case KnownTypeCode.Char:
case KnownTypeCode.SByte:
case KnownTypeCode.Byte:
case KnownTypeCode.Int16:
case KnownTypeCode.UInt16:
case KnownTypeCode.Int32:
case KnownTypeCode.UInt32:
case KnownTypeCode.Int64:
case KnownTypeCode.UInt64:
case KnownTypeCode.Single:
case KnownTypeCode.Double:
case KnownTypeCode.Decimal:
case KnownTypeCode.IntPtr:
case KnownTypeCode.UIntPtr:
case KnownTypeCode.String:
return true;
default:
return false;
}
}
static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name)

28
ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

@ -283,10 +283,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -283,10 +283,9 @@ namespace ICSharpCode.Decompiler.CSharp
UnwrapChild(uoe.Expression).ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion)
).WithRR(new ResolveResult(targetType)).WithoutILInstruction();
}
bool isLifted = type.IsKnownType(KnownTypeCode.NullableOfT) && targetType.IsKnownType(KnownTypeCode.NullableOfT);
IType utype = isLifted ? NullableType.GetUnderlyingType(type) : type;
IType targetUType = isLifted ? NullableType.GetUnderlyingType(targetType) : targetType;
if (type.IsKnownType(KnownTypeCode.Boolean) && targetType.GetStackType().IsIntegerType()) {
IType utype = NullableType.GetUnderlyingType(type);
IType targetUType = NullableType.GetUnderlyingType(targetType);
if (type.IsKnownType(KnownTypeCode.Boolean) && targetUType.GetStackType().IsIntegerType()) {
// convert from boolean to integer (or enum)
return new ConditionalExpression(
this.Expression,
@ -318,7 +317,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -318,7 +317,7 @@ namespace ICSharpCode.Decompiler.CSharp
.ConvertTo(targetType, expressionBuilder, checkForOverflow);
}
}
if (targetType.IsKnownType(KnownTypeCode.IntPtr)) { // Conversion to IntPtr
if (targetUType.IsKnownType(KnownTypeCode.IntPtr)) { // Conversion to IntPtr
if (type.IsKnownType(KnownTypeCode.Int32)) {
// normal casts work for int (both in checked and unchecked context)
} else if (checkForOverflow) {
@ -336,7 +335,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -336,7 +335,7 @@ namespace ICSharpCode.Decompiler.CSharp
.ConvertTo(targetType, expressionBuilder, checkForOverflow);
}
}
} else if (targetType.IsKnownType(KnownTypeCode.UIntPtr)) { // Conversion to UIntPtr
} else if (targetUType.IsKnownType(KnownTypeCode.UIntPtr)) { // Conversion to UIntPtr
if (type.IsKnownType(KnownTypeCode.UInt32) || type.Kind == TypeKind.Pointer) {
// normal casts work for uint and pointers (both in checked and unchecked context)
} else if (checkForOverflow) {
@ -359,14 +358,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -359,14 +358,14 @@ namespace ICSharpCode.Decompiler.CSharp
// -> convert via underlying type
return this.ConvertTo(type.GetEnumUnderlyingType(), expressionBuilder, checkForOverflow)
.ConvertTo(targetType, expressionBuilder, checkForOverflow);
} else if (targetType.Kind == TypeKind.Enum && type.Kind == TypeKind.Pointer) {
} else if (targetUType.Kind == TypeKind.Enum && type.Kind == TypeKind.Pointer) {
// pointer to enum: C# doesn't allow such casts
// -> convert via underlying type
return this.ConvertTo(targetType.GetEnumUnderlyingType(), expressionBuilder, checkForOverflow)
return this.ConvertTo(targetUType.GetEnumUnderlyingType(), expressionBuilder, checkForOverflow)
.ConvertTo(targetType, expressionBuilder, checkForOverflow);
}
if (targetType.Kind == TypeKind.Pointer && type.IsKnownType(KnownTypeCode.Char)
|| targetType.IsKnownType(KnownTypeCode.Char) && type.Kind == TypeKind.Pointer) {
|| targetUType.IsKnownType(KnownTypeCode.Char) && type.Kind == TypeKind.Pointer) {
// char <-> pointer: C# doesn't allow such casts
// -> convert via ushort
return this.ConvertTo(compilation.FindType(KnownTypeCode.UInt16), expressionBuilder, checkForOverflow)
@ -418,6 +417,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -418,6 +417,17 @@ namespace ICSharpCode.Decompiler.CSharp
.WithoutILInstruction()
.WithRR(new ByReferenceResolveResult(elementRR, ReferenceKind.Ref));
}
if (this.ResolveResult.IsCompileTimeConstant && this.ResolveResult.ConstantValue != null
&& NullableType.IsNullable(targetType) && !utype.Equals(targetUType))
{
// Casts like `(uint?)-1` are only valid in an explicitly unchecked context, but we
// don't have logic to ensure such a context (usually we emit into an implicitly unchecked context).
// This only applies with constants as input (int->uint? is fine in implicitly unchecked context).
// We use an intermediate cast to the nullable's underlying type, which results
// in a constant conversion, so the final output will be something like `(uint?)uint.MaxValue`
return ConvertTo(targetUType, expressionBuilder, checkForOverflow, allowImplicitConversion: false)
.ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion);
}
var rr = expressionBuilder.resolver.WithCheckForOverflow(checkForOverflow).ResolveCast(targetType, ResolveResult);
if (rr.IsCompileTimeConstant && !rr.IsError) {
return expressionBuilder.ConvertConstantValue(rr, allowImplicitConversion)

2
ICSharpCode.Decompiler/DebugInfo/SequencePoint.cs

@ -17,12 +17,14 @@ @@ -17,12 +17,14 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Diagnostics;
namespace ICSharpCode.Decompiler.DebugInfo
{
/// <summary>
/// A sequence point read from a PDB file or produced by the decompiler.
/// </summary>
[DebuggerDisplay("SequencePoint IL_{Offset,h}-IL_{EndOffset,h}, {StartLine}:{StartColumn}-{EndLine}:{EndColumn}, IsHidden={IsHidden}")]
public struct SequencePoint
{
/// <summary>

17
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -107,12 +107,13 @@ namespace ICSharpCode.Decompiler @@ -107,12 +107,13 @@ namespace ICSharpCode.Decompiler
}
if (languageVersion < CSharp.LanguageVersion.CSharp8_0) {
nullableReferenceTypes = false;
readOnlyMethods = false;
}
}
public CSharp.LanguageVersion GetMinimumRequiredVersion()
{
if (nullableReferenceTypes)
if (nullableReferenceTypes || readOnlyMethods)
return CSharp.LanguageVersion.CSharp8_0;
if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers)
return CSharp.LanguageVersion.CSharp7_3;
@ -842,6 +843,20 @@ namespace ICSharpCode.Decompiler @@ -842,6 +843,20 @@ namespace ICSharpCode.Decompiler
}
}
bool readOnlyMethods = true;
[Category("C# 8.0 / VS 2019")]
[Description("DecompilerSettings.IsReadOnlyAttributeShouldBeReplacedWithReadonlyInModifiersOnStructsParameters")]
public bool ReadOnlyMethods {
get { return readOnlyMethods; }
set {
if (readOnlyMethods != value) {
readOnlyMethods = value;
OnPropertyChanged();
}
}
}
bool introduceUnmanagedConstraint = true;
/// <summary>

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -328,6 +328,7 @@ @@ -328,6 +328,7 @@
<Compile Include="IL\Transforms\HighLevelLoopTransform.cs" />
<Compile Include="IL\Transforms\NamedArgumentTransform.cs" />
<Compile Include="IL\Transforms\IntroduceDynamicTypeOnLocals.cs" />
<Compile Include="IL\Transforms\IntroduceRefReadOnlyModifierOnLocals.cs" />
<Compile Include="IL\Transforms\NullPropagationTransform.cs" />
<Compile Include="IL\Transforms\ProxyCallReplacer.cs" />
<Compile Include="IL\Instructions\StringToInt.cs" />

22
ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

@ -85,6 +85,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -85,6 +85,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
TryCatch mainTryCatch;
Block setResultAndExitBlock; // block that is jumped to for return statements
int finalState; // final state after the setResultAndExitBlock
bool finalStateKnown;
ILVariable resultVar; // the variable that gets returned by the setResultAndExitBlock
ILVariable doFinallyBodies;
@ -352,7 +353,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -352,7 +353,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
moveNextFunction = YieldReturnDecompiler.CreateILAst(moveNextMethod, context);
if (!(moveNextFunction.Body is BlockContainer blockContainer))
throw new SymbolicAnalysisFailedException();
if (blockContainer.Blocks.Count != 2)
if (blockContainer.Blocks.Count != 2 && blockContainer.Blocks.Count != 1)
throw new SymbolicAnalysisFailedException();
if (blockContainer.EntryPoint.IncomingEdgeCount != 1)
throw new SymbolicAnalysisFailedException();
@ -389,7 +390,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -389,7 +390,14 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
doFinallyBodies = initDoFinallyBodies.Variable;
}
setResultAndExitBlock = blockContainer.Blocks[1];
setResultAndExitBlock = blockContainer.Blocks.ElementAtOrDefault(1);
if (setResultAndExitBlock == null) {
// This block can be absent if the function never exits normally,
// but always throws an exception/loops infinitely.
resultVar = null;
finalStateKnown = false; // final state will be detected in ValidateCatchBlock() instead
return;
}
// stobj System.Int32(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+<SimpleBoolTaskMethod>d__7.<>1__state](ldloc this), ldc.i4 -2)
// call SetResult(ldflda [Field ICSharpCode.Decompiler.Tests.TestCases.Pretty.Async+<SimpleBoolTaskMethod>d__7.<>t__builder](ldloc this), ldloc result)
// leave IL_0000
@ -397,6 +405,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -397,6 +405,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
throw new SymbolicAnalysisFailedException();
if (!MatchStateAssignment(setResultAndExitBlock.Instructions[0], out finalState))
throw new SymbolicAnalysisFailedException();
finalStateKnown = true;
if (!MatchCall(setResultAndExitBlock.Instructions[1], "SetResult", out var args))
throw new SymbolicAnalysisFailedException();
if (!IsBuilderFieldOnThis(args[0]))
@ -442,8 +451,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -442,8 +451,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
if (!stloc.Value.MatchLdLoc(handler.Variable))
throw new SymbolicAnalysisFailedException();
// stfld <>1__state(ldloc this, ldc.i4 -2)
if (!MatchStateAssignment(catchBlock.Instructions[1], out int newState) || newState != finalState)
if (!MatchStateAssignment(catchBlock.Instructions[1], out int newState))
throw new SymbolicAnalysisFailedException();
if (finalStateKnown) {
if (newState != finalState)
throw new SymbolicAnalysisFailedException();
} else {
finalState = newState;
finalStateKnown = true;
}
// call SetException(ldfld <>t__builder(ldloc this), ldloc exception)
if (!MatchCall(catchBlock.Instructions[2], "SetException", out var args))
throw new SymbolicAnalysisFailedException();

11
ICSharpCode.Decompiler/IL/ILReader.cs

@ -190,12 +190,15 @@ namespace ICSharpCode.Decompiler.IL @@ -190,12 +190,15 @@ namespace ICSharpCode.Decompiler.IL
// and needs to be converted into a normally usable type.
declaringType = new ParameterizedType(declaringType, declaringType.TypeParameters);
}
parameterVariables[paramIndex++] = CreateILVariable(-1, declaringType, "this");
ILVariable ilVar = CreateILVariable(-1, declaringType, "this");
ilVar.IsRefReadOnly = method.ThisIsRefReadOnly;
parameterVariables[paramIndex++] = ilVar;
}
while (paramIndex < parameterVariables.Length) {
IType type = method.Parameters[paramIndex - offset].Type;
string name = method.Parameters[paramIndex - offset].Name;
parameterVariables[paramIndex] = CreateILVariable(paramIndex - offset, type, name);
IParameter parameter = method.Parameters[paramIndex - offset];
ILVariable ilVar = CreateILVariable(paramIndex - offset, parameter.Type, parameter.Name);
ilVar.IsRefReadOnly = parameter.IsIn;
parameterVariables[paramIndex] = ilVar;
paramIndex++;
}
Debug.Assert(paramIndex == parameterVariables.Length);

8
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -132,6 +132,11 @@ namespace ICSharpCode.Decompiler.IL @@ -132,6 +132,11 @@ namespace ICSharpCode.Decompiler.IL
}
}
/// <summary>
/// This variable is either a C# 7 'in' parameter or must be declared as 'ref readonly'.
/// </summary>
public bool IsRefReadOnly { get; internal set; }
/// <summary>
/// The index of the local variable or parameter (depending on Kind)
///
@ -371,6 +376,9 @@ namespace ICSharpCode.Decompiler.IL @@ -371,6 +376,9 @@ namespace ICSharpCode.Decompiler.IL
internal void WriteDefinitionTo(ITextOutput output)
{
if (IsRefReadOnly) {
output.Write("readonly ");
}
switch (Kind) {
case VariableKind.Local:
output.Write("local ");

32
ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@ -31,7 +32,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -31,7 +32,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// then we can replace the variable with the argument.
/// 2) assignments of address-loading instructions to local variables
/// </summary>
public class CopyPropagation : IBlockTransform, IILTransform
public class CopyPropagation : IILTransform
{
public static void Propagate(StLoc store, ILTransformContext context)
{
@ -41,26 +42,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -41,26 +42,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
DoPropagate(store.Variable, store.Value, block, ref i, context);
}
public void Run(Block block, BlockTransformContext context)
{
RunOnBlock(block, context);
}
public void Run(ILFunction function, ILTransformContext context)
{
var splitVariables = new HashSet<ILVariable>(ILVariableEqualityComparer.Instance);
foreach (var g in function.Variables.GroupBy(v => v, ILVariableEqualityComparer.Instance)) {
if (g.Count() > 1) {
splitVariables.Add(g.Key);
}
}
foreach (var block in function.Descendants.OfType<Block>()) {
if (block.Kind != BlockKind.ControlFlow)
continue;
RunOnBlock(block, context);
RunOnBlock(block, context, splitVariables);
}
}
static void RunOnBlock(Block block, ILTransformContext context)
static void RunOnBlock(Block block, ILTransformContext context, HashSet<ILVariable> splitVariables = null)
{
for (int i = 0; i < block.Instructions.Count; i++) {
ILVariable v;
ILInstruction copiedExpr;
if (block.Instructions[i].MatchStLoc(out v, out copiedExpr)) {
if (block.Instructions[i].MatchStLoc(out ILVariable v, out ILInstruction copiedExpr)) {
if (v.IsSingleDefinition && v.LoadCount == 0 && v.Kind == VariableKind.StackSlot) {
// dead store to stack
if (SemanticHelper.IsPure(copiedExpr.Flags)) {
@ -76,18 +76,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -76,18 +76,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms
copiedExpr.AddILRange(block.Instructions[i]);
block.Instructions[i] = copiedExpr;
}
} else if (v.IsSingleDefinition && CanPerformCopyPropagation(v, copiedExpr)) {
} else if (v.IsSingleDefinition && CanPerformCopyPropagation(v, copiedExpr, splitVariables)) {
DoPropagate(v, copiedExpr, block, ref i, context);
}
}
}
}
static bool CanPerformCopyPropagation(ILVariable target, ILInstruction value)
static bool CanPerformCopyPropagation(ILVariable target, ILInstruction value, HashSet<ILVariable> splitVariables)
{
Debug.Assert(target.StackType == value.ResultType);
if (target.Type.IsSmallIntegerType())
return false;
if (splitVariables != null && splitVariables.Contains(target)) {
return false; // non-local code move might change semantics when there's split variables
}
switch (value.OpCode) {
case OpCode.LdLoca:
// case OpCode.LdElema:
@ -100,6 +103,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -100,6 +103,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
case OpCode.LdLoc:
var v = ((LdLoc)value).Variable;
if (splitVariables != null && splitVariables.Contains(v)) {
return false; // non-local code move might change semantics when there's split variables
}
switch (v.Kind) {
case VariableKind.Parameter:
// Parameters can be copied only if they aren't assigned to (directly or indirectly via ldarga)

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

@ -249,7 +249,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -249,7 +249,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Thus, we have to ensure we're operating on an r-value.
// Additionally, we cannot inline in cases where the C# compiler prohibits the direct use
// of the rvalue (e.g. M(ref (MyStruct)obj); is invalid).
if (!IsUsedAsThisPointerInCall(loadInst))
if (!IsUsedAsThisPointerInCall(loadInst, out var method))
return false;
switch (ClassifyExpression(inlinedExpression)) {
case ExpressionClassification.RValue:
@ -261,14 +261,30 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -261,14 +261,30 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case ExpressionClassification.ReadonlyLValue:
// For struct method calls on readonly lvalues, the C# compiler
// only generates a temporary if it isn't a "readonly struct"
return !(v.Type.GetDefinition()?.IsReadOnly == true);
return MethodRequiresCopyForReadonlyLValue(method);
default:
throw new InvalidOperationException("invalid expression classification");
}
}
internal static bool MethodRequiresCopyForReadonlyLValue(IMethod method)
{
var type = method.DeclaringType;
if (type.IsReferenceType == true)
return false; // reference types are never implicitly copied
if (method.ThisIsRefReadOnly)
return false; // no copies for calls on readonly structs
return true;
}
internal static bool IsUsedAsThisPointerInCall(LdLoca ldloca)
{
return IsUsedAsThisPointerInCall(ldloca, out _);
}
static bool IsUsedAsThisPointerInCall(LdLoca ldloca, out IMethod method)
{
method = null;
if (ldloca.ChildIndex != 0)
return false;
if (ldloca.Variable.Type.IsReferenceType ?? false)
@ -280,7 +296,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -280,7 +296,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
switch (inst.Parent.OpCode) {
case OpCode.Call:
case OpCode.CallVirt:
var method = ((CallInstruction)inst.Parent).Method;
method = ((CallInstruction)inst.Parent).Method;
if (method.IsAccessor && method.AccessorKind != MethodSemanticsAttributes.Getter) {
// C# doesn't allow calling setters on temporary structs
return false;
@ -313,7 +329,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -313,7 +329,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
switch (inst.OpCode) {
case OpCode.LdLoc:
case OpCode.StLoc:
if (IsReadonlyRefLocal(((IInstructionWithVariableOperand)inst).Variable)) {
if (((IInstructionWithVariableOperand)inst).Variable.IsRefReadOnly) {
return ExpressionClassification.ReadonlyLValue;
} else {
return ExpressionClassification.MutableLValue;
@ -347,7 +363,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -347,7 +363,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
private static bool IsReadonlyReference(ILInstruction addr)
internal static bool IsReadonlyReference(ILInstruction addr)
{
switch (addr) {
case LdFlda ldflda:
@ -356,7 +372,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -356,7 +372,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case LdsFlda ldsflda:
return ldsflda.Field.IsReadOnly;
case LdLoc ldloc:
return IsReadonlyRefLocal(ldloc.Variable);
return ldloc.Variable.IsRefReadOnly;
case Call call:
return call.Method.ReturnTypeIsRefReadOnly;
case AddressOf _:
@ -367,19 +383,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -367,19 +383,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
private static bool IsReadonlyRefLocal(ILVariable variable)
{
if (variable.Kind == VariableKind.Parameter) {
if (variable.Index == -1) {
// this parameter in readonly struct
return variable.Function.Method?.DeclaringTypeDefinition?.IsReadOnly == true;
} else {
return variable.Function.Parameters[variable.Index.Value].IsIn;
}
}
return false;
}
/// <summary>
/// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable.
/// </summary>

57
ICSharpCode.Decompiler/IL/Transforms/IntroduceRefReadOnlyModifierOnLocals.cs

@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
// Copyright (c) 2018 Siegfried Pammer
//
// 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.Linq;
using System.Text;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
class IntroduceRefReadOnlyModifierOnLocals : IILTransform
{
public void Run(ILFunction function, ILTransformContext context)
{
foreach (var variable in function.Variables) {
if (variable.Type.Kind != TypeKind.ByReference || variable.Kind == VariableKind.Parameter)
continue;
// ref readonly
if (IsUsedAsRefReadonly(variable)) {
variable.IsRefReadOnly = true;
continue;
}
}
}
/// <summary>
/// Infer ref readonly type from usage:
/// An ILVariable should be marked as readonly,
/// if it's a "by-ref-like" type and the initialized value is known to be readonly.
/// </summary>
bool IsUsedAsRefReadonly(ILVariable variable)
{
foreach (var store in variable.StoreInstructions.OfType<StLoc>()) {
if (ILInlining.IsReadonlyReference(store.Value))
return true;
}
return false;
}
}
}

189
ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs

@ -37,26 +37,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -37,26 +37,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public ILInstruction Initializer;
public ILVariable Variable;
public ITypeDefinition Definition;
public Dictionary<IField, DisplayClassVariable> Variables;
public Dictionary<IField, ILVariable> Variables;
public BlockContainer CaptureScope;
public ILFunction DeclaringFunction;
}
struct DisplayClassVariable
{
public ILVariable Variable;
public ILInstruction Value;
}
ILTransformContext context;
ILFunction currentFunction;
readonly Dictionary<ILVariable, DisplayClass> displayClasses = new Dictionary<ILVariable, DisplayClass>();
readonly List<ILInstruction> instructionsToRemove = new List<ILInstruction>();
public void Run(ILFunction function, ILTransformContext context)
{
try {
if (this.context != null || this.currentFunction != null)
if (this.context != null)
throw new InvalidOperationException("Reentrancy in " + nameof(TransformDisplayClassUsage));
this.context = context;
var decompilationContext = new SimpleTypeResolveContext(context.Function.Method);
@ -73,12 +66,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -73,12 +66,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body, localFunctionClosureParameter: true);
}
}
foreach (var displayClass in displayClasses.Values.OrderByDescending(d => d.Initializer.StartILOffset).ToArray()) {
context.Step($"Transform references to " + displayClass.Variable, displayClass.Initializer);
this.currentFunction = f;
VisitILFunction(f);
}
}
VisitILFunction(function);
if (instructionsToRemove.Count > 0) {
context.Step($"Remove instructions", function);
foreach (var store in instructionsToRemove) {
@ -91,7 +80,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -91,7 +80,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
instructionsToRemove.Clear();
displayClasses.Clear();
this.context = null;
this.currentFunction = null;
}
}
@ -106,7 +94,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -106,7 +94,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
Initializer = inst,
Variable = v,
Definition = closureType,
Variables = new Dictionary<IField, DisplayClassVariable>(),
Variables = new Dictionary<IField, ILVariable>(),
CaptureScope = (isMono && IsMonoNestedCaptureScope(closureType)) || localFunctionClosureParameter ? null : v.CaptureScope,
DeclaringFunction = localFunctionClosureParameter ? f.DeclarationScope.Ancestors.OfType<ILFunction>().First() : f
});
@ -151,11 +139,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -151,11 +139,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
}
bool IsOuterClosureReference(IField field)
{
return displayClasses.Values.Any(disp => disp.Definition == field.DeclaringTypeDefinition);
}
bool IsMonoNestedCaptureScope(ITypeDefinition closureType)
{
var decompilationContext = new SimpleTypeResolveContext(context.Function.Method);
@ -181,17 +164,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -181,17 +164,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
Initializer = nestedFunction.Body,
Variable = thisVariable,
Definition = thisVariable.Type.GetDefinition(),
Variables = new Dictionary<IField, DisplayClassVariable>(),
Variables = new Dictionary<IField, ILVariable>(),
CaptureScope = (BlockContainer)nestedFunction.Body
};
displayClasses.Add(thisVariable, displayClass);
foreach (var stateMachineVariable in nestedFunction.Variables) {
if (stateMachineVariable.StateMachineField == null || displayClass.Variables.ContainsKey(stateMachineVariable.StateMachineField))
continue;
displayClass.Variables.Add(stateMachineVariable.StateMachineField, new DisplayClassVariable {
Variable = stateMachineVariable,
Value = new LdLoc(stateMachineVariable)
});
displayClass.Variables.Add(stateMachineVariable.StateMachineField, stateMachineVariable);
}
if (!currentFunction.Method.IsStatic && FindThisField(out var thisField)) {
var thisVar = currentFunction.Variables
@ -200,7 +180,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -200,7 +180,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
thisVar = new ILVariable(VariableKind.Parameter, decompilationContext.CurrentTypeDefinition, -1) { Name = "this" };
currentFunction.Variables.Add(thisVar);
}
displayClass.Variables.Add(thisField, new DisplayClassVariable { Variable = thisVar, Value = new LdLoc(thisVar) });
displayClass.Variables.Add(thisField, thisVar);
}
return true;
@ -263,11 +243,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -263,11 +243,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
bool IsDisplayClassLoad(ILInstruction target, out ILVariable variable)
ILFunction currentFunction;
protected internal override void VisitILFunction(ILFunction function)
{
if (target.MatchLdLoc(out variable) || target.MatchLdLoca(out variable))
return true;
return false;
var oldFunction = this.currentFunction;
try {
this.currentFunction = function;
base.VisitILFunction(function);
} finally {
this.currentFunction = oldFunction;
}
}
protected override void Default(ILInstruction inst)
@ -280,105 +266,102 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -280,105 +266,102 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitStLoc(StLoc inst)
{
base.VisitStLoc(inst);
// Sometimes display class references are copied into other local variables.
// We remove the assignment and store the relationship between the display class and the variable in the
// displayClasses dictionary.
if (inst.Value.MatchLdLoc(out var closureVariable) && displayClasses.TryGetValue(closureVariable, out var displayClass)) {
displayClasses[inst.Variable] = displayClass;
instructionsToRemove.Add(inst);
} else if (inst.Variable.Kind == VariableKind.Local && inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 0 && inst.Value is StLoc) {
if (inst.Variable.Kind == VariableKind.Local && inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 0 && inst.Value is StLoc) {
context.Step($"Remove unused variable assignment {inst.Variable.Name}", inst);
inst.ReplaceWith(inst.Value);
}
}
protected internal override void VisitStObj(StObj inst)
{
base.VisitStObj(inst);
// This instruction has been marked deletable, do not transform it further
if (instructionsToRemove.Contains(inst))
return;
// The target of the store instruction must be a field reference
if (!inst.Target.MatchLdFlda(out ILInstruction target, out IField field))
return;
// Get display class info
if (!IsDisplayClassLoad(target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass))
return;
field = (IField)field.MemberDefinition;
if (displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) {
// If the display class field was previously initialized, we use a simple assignment.
inst.ReplaceWith(new StLoc(info.Variable, inst.Value).WithILRange(inst));
inst.Value.AcceptVisitor(this);
if (IsParameterAssignment(inst, out var displayClass, out var field, out var parameter)) {
context.Step($"Detected parameter assignment {parameter.Name}", inst);
displayClass.Variables.Add(field, parameter);
instructionsToRemove.Add(inst);
} else if (IsDisplayClassAssignment(inst, out displayClass, out field, out var variable)) {
context.Step($"Detected display-class assignment {variable.Name}", inst);
displayClass.Variables.Add(field, variable);
instructionsToRemove.Add(inst);
} else {
// This is an uninitialized variable:
ILInstruction value;
if (inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && currentFunction == v.Function) {
// Special case for parameters: remove copies of parameter values.
instructionsToRemove.Add(inst);
value = inst.Value;
} else {
context.Step($"Introduce captured variable for {field.FullName}", inst);
Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition);
// Introduce a fresh variable for the display class field.
if (displayClass.IsMono && displayClass.CaptureScope == null && !IsOuterClosureReference(field)) {
displayClass.CaptureScope = BlockContainer.FindClosestContainer(inst);
}
v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name);
v.HasInitialValue = true;
v.CaptureScope = displayClass.CaptureScope;
inst.ReplaceWith(new StLoc(v, inst.Value).WithILRange(inst));
value = new LdLoc(v);
}
displayClass.Variables.Add(field, new DisplayClassVariable { Value = value, Variable = v });
inst.Target.AcceptVisitor(this);
EarlyExpressionTransforms.StObjToStLoc(inst, context);
}
}
protected internal override void VisitLdObj(LdObj inst)
{
base.VisitLdObj(inst);
// The target of the store instruction must be a field reference
if (!inst.Target.MatchLdFlda(out var target, out IField field))
return;
// Get display class info
if (!IsDisplayClassLoad(target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass))
return;
// Get display class variable info
if (!displayClass.Variables.TryGetValue((IField)field.MemberDefinition, out DisplayClassVariable info))
return;
// Replace usage of display class field with the variable.
var replacement = info.Value.Clone();
replacement.SetILRange(inst);
inst.ReplaceWith(replacement);
EarlyExpressionTransforms.LdObjToLdLoc(inst, context);
}
private bool IsDisplayClassLoad(ILInstruction target, out ILVariable variable)
{
// We cannot use MatchLdLocRef here because local functions use ref parameters
if (target.MatchLdLoc(out variable) || target.MatchLdLoca(out variable))
return true;
return false;
}
private bool IsDisplayClassAssignment(StObj inst, out DisplayClass displayClass, out IField field, out ILVariable variable)
{
variable = null;
if (!IsDisplayClassFieldAccess(inst.Target, out var displayClassVar, out displayClass, out field))
return false;
if (!(inst.Value.MatchLdLoc(out var v) && displayClasses.ContainsKey(v)))
return false;
if (displayClassVar.Function != currentFunction)
return false;
variable = v;
return true;
}
private bool IsParameterAssignment(StObj inst, out DisplayClass displayClass, out IField field, out ILVariable parameter)
{
parameter = null;
if (!IsDisplayClassFieldAccess(inst.Target, out var displayClassVar, out displayClass, out field))
return false;
if (!(inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && v.Function == currentFunction))
return false;
if (displayClass.Variables.ContainsKey(field))
return false;
if (displayClassVar.Function != currentFunction)
return false;
parameter = v;
return true;
}
private bool IsDisplayClassFieldAccess(ILInstruction inst, out ILVariable displayClassVar, out DisplayClass displayClass, out IField field)
{
displayClass = null;
displayClassVar = null;
field = null;
if (!(inst is LdFlda ldflda))
return false;
field = (IField)ldflda.Field.MemberDefinition;
return IsDisplayClassLoad(ldflda.Target, out displayClassVar)
&& displayClasses.TryGetValue(displayClassVar, out displayClass);
}
protected internal override void VisitLdFlda(LdFlda inst)
{
base.VisitLdFlda(inst);
// TODO : Figure out why this was added in https://github.com/icsharpcode/ILSpy/pull/1303
if (inst.Target.MatchLdThis() && inst.Field.Name == "$this"
&& inst.Field.MemberDefinition.ReflectionName.Contains("c__Iterator")) {
//Debug.Assert(false, "This should not be executed!");
var variable = currentFunction.Variables.First((f) => f.Index == -1);
inst.ReplaceWith(new LdLoca(variable).WithILRange(inst));
}
// Skip stfld/ldfld
if (inst.Parent is LdObj || inst.Parent is StObj)
return;
// Get display class info
if (!IsDisplayClassLoad(inst.Target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass))
if (!IsDisplayClassFieldAccess(inst, out _, out DisplayClass displayClass, out IField field))
return;
var field = (IField)inst.Field.MemberDefinition;
if (!displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) {
if (!displayClass.Variables.TryGetValue(field, out var v)) {
context.Step($"Introduce captured variable for {field.FullName}", inst);
// Introduce a fresh variable for the display class field.
Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition);
var v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name);
v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name);
v.HasInitialValue = true;
v.CaptureScope = displayClass.CaptureScope;
inst.ReplaceWith(new LdLoca(v).WithILRange(inst));
displayClass.Variables.Add(field, new DisplayClassVariable { Value = new LdLoc(v), Variable = v });
} else if (info.Value is LdLoc l) {
inst.ReplaceWith(new LdLoca(l.Variable).WithILRange(inst));
displayClass.Variables.Add(field, v);
} else {
Debug.Fail("LdFlda pattern not supported!");
context.Step($"Reuse captured variable {v.Name} for {field.FullName}", inst);
inst.ReplaceWith(new LdLoca(v).WithILRange(inst));
}
}
}

24
ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs

@ -53,6 +53,13 @@ namespace ICSharpCode.Decompiler.Metadata @@ -53,6 +53,13 @@ namespace ICSharpCode.Decompiler.Metadata
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages")
};
static readonly string[] RuntimePacks = new[] {
"Microsoft.NETCore.App",
"Microsoft.WindowsDesktop.App",
"Microsoft.AspNetCore.App",
"Microsoft.AspNetCore.All"
};
readonly Dictionary<string, DotNetCorePackageInfo> packages;
ISet<string> packageBasePaths = new HashSet<string>(StringComparer.Ordinal);
readonly string assemblyName;
@ -124,13 +131,16 @@ namespace ICSharpCode.Decompiler.Metadata @@ -124,13 +131,16 @@ namespace ICSharpCode.Decompiler.Metadata
string FallbackToDotNetSharedDirectory(IAssemblyReference name, Version version)
{
if (dotnetBasePath == null) return null;
var basePath = Path.Combine(dotnetBasePath, "shared", "Microsoft.NETCore.App");
var closestVersion = GetClosestVersionFolder(basePath, version);
if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".dll"))) {
return Path.Combine(basePath, closestVersion, name.Name + ".dll");
} else if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".exe"))) {
return Path.Combine(basePath, closestVersion, name.Name + ".exe");
if (dotnetBasePath == null)
return null;
var basePaths = RuntimePacks.Select(pack => Path.Combine(dotnetBasePath, "shared", pack));
foreach (var basePath in basePaths) {
var closestVersion = GetClosestVersionFolder(basePath, version);
if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".dll"))) {
return Path.Combine(basePath, closestVersion, name.Name + ".dll");
} else if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".exe"))) {
return Path.Combine(basePath, closestVersion, name.Name + ".exe");
}
}
return null;
}

11
ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs

@ -49,7 +49,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -49,7 +49,7 @@ namespace ICSharpCode.Decompiler.Metadata
return publicKeyTokenBytes.TakeLast(8).Reverse().ToHexString(8);
}
public static string GetFullAssemblyName(this MetadataReader reader)
public static string GetPublicKeyToken(this MetadataReader reader)
{
if (!reader.IsAssembly)
return string.Empty;
@ -59,6 +59,15 @@ namespace ICSharpCode.Decompiler.Metadata @@ -59,6 +59,15 @@ namespace ICSharpCode.Decompiler.Metadata
// AssemblyFlags.PublicKey does not apply to assembly definitions
publicKey = CalculatePublicKeyToken(asm.PublicKey, reader);
}
return publicKey;
}
public static string GetFullAssemblyName(this MetadataReader reader)
{
if (!reader.IsAssembly)
return string.Empty;
var asm = reader.GetAssemblyDefinition();
string publicKey = reader.GetPublicKeyToken();
return $"{reader.GetString(asm.Name)}, " +
$"Version={asm.Version}, " +
$"Culture={(asm.Culture.IsNil ? "neutral" : reader.GetString(asm.Culture))}, " +

12
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -84,7 +84,8 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -84,7 +84,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
KeepModifiers = 0x40,
/// <summary>
/// If this option is active, [IsReadOnlyAttribute] is removed and parameters are marked as in, structs as readonly.
/// If this option is active, [IsReadOnlyAttribute] on parameters+structs is removed
/// and parameters are marked as in, structs as readonly.
/// Otherwise, the attribute is preserved but the parameters and structs are not marked.
/// </summary>
ReadOnlyStructsAndParameters = 0x80,
@ -104,10 +105,15 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -104,10 +105,15 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
NullabilityAnnotations = 0x400,
/// <summary>
/// If this option is active, [IsReadOnlyAttribute] on methods is removed
/// and the method marked as ThisIsRefReadOnly.
/// </summary>
ReadOnlyMethods = 0x800,
/// <summary>
/// Default settings: typical options for the decompiler, with all C# languages features enabled.
/// </summary>
Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters
| RefStructs | UnmanagedConstraints | NullabilityAnnotations
| RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods
}
/// <summary>
@ -137,6 +143,8 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -137,6 +143,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
typeSystemOptions |= TypeSystemOptions.UnmanagedConstraints;
if (settings.NullableReferenceTypes)
typeSystemOptions |= TypeSystemOptions.NullabilityAnnotations;
if (settings.ReadOnlyMethods)
typeSystemOptions |= TypeSystemOptions.ReadOnlyMethods;
return typeSystemOptions;
}

6
ICSharpCode.Decompiler/TypeSystem/IMethod.cs

@ -40,6 +40,12 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -40,6 +40,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
bool ReturnTypeIsRefReadOnly { get; }
/// <summary>
/// Gets whether the method accepts the 'this' reference as ref readonly.
/// This can be either because the method is C# 8.0 'readonly', or because it is within a C# 7.2 'readonly struct'
/// </summary>
bool ThisIsRefReadOnly { get; }
/// <summary>
/// Gets the type parameters of this method; or an empty list if the method is not generic.
/// </summary>

11
ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs

@ -201,7 +201,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -201,7 +201,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
case "DecimalConstantAttribute":
return (options & TypeSystemOptions.DecimalConstants) != 0 && (target == SymbolKind.Field || target == SymbolKind.Parameter);
case "IsReadOnlyAttribute":
return (options & TypeSystemOptions.ReadOnlyStructsAndParameters) != 0;
switch (target) {
case SymbolKind.TypeDefinition:
case SymbolKind.Parameter:
return (options & TypeSystemOptions.ReadOnlyStructsAndParameters) != 0;
case SymbolKind.Method:
case SymbolKind.Accessor:
return (options & TypeSystemOptions.ReadOnlyMethods) != 0;
default:
return false;
}
case "IsByRefLikeAttribute":
return (options & TypeSystemOptions.RefStructs) != 0 && target == SymbolKind.TypeDefinition;
case "IsUnmanagedAttribute":

1
ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs

@ -133,6 +133,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -133,6 +133,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => EmptyList<IAttribute>.Instance;
bool IMethod.ReturnTypeIsRefReadOnly => false;
bool IMethod.ThisIsRefReadOnly => false;
public IReadOnlyList<ITypeParameter> TypeParameters { get; set; } = EmptyList<ITypeParameter>.Instance;

1
ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs

@ -117,6 +117,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -117,6 +117,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IEnumerable<IAttribute> IEntity.GetAttributes() => baseMethod.GetAttributes();
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes();
bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly;
bool IMethod.ThisIsRefReadOnly => baseMethod.ThisIsRefReadOnly;
/// <summary>
/// We consider local functions as always static, because they do not have a "this parameter".
/// Even local functions in instance methods capture this.

18
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs

@ -49,6 +49,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -49,6 +49,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IParameter[] parameters;
IType returnType;
byte returnTypeIsRefReadonly = ThreeState.Unknown;
byte thisIsRefReadonly = ThreeState.Unknown;
internal MetadataMethod(MetadataModule module, MethodDefinitionHandle handle)
{
@ -424,6 +425,23 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -424,6 +425,23 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return hasReadOnlyAttr;
}
}
public bool ThisIsRefReadOnly {
get {
if (thisIsRefReadonly != ThreeState.Unknown) {
return thisIsRefReadonly == ThreeState.True;
}
var metadata = module.metadata;
var methodDefinition = metadata.GetMethodDefinition(handle);
bool hasReadOnlyAttr = DeclaringTypeDefinition?.IsReadOnly ?? false;
if ((module.TypeSystemOptions & TypeSystemOptions.ReadOnlyMethods) != 0) {
hasReadOnlyAttr |= methodDefinition.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsReadOnly);
}
this.thisIsRefReadonly = ThreeState.From(hasReadOnlyAttr);
return hasReadOnlyAttr;
}
}
#endregion
public Accessibility Accessibility => GetAccessibility(attributes);

2
ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs

@ -97,6 +97,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -97,6 +97,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public IEnumerable<IAttribute> GetReturnTypeAttributes() => methodDefinition.GetReturnTypeAttributes();
public bool ReturnTypeIsRefReadOnly => methodDefinition.ReturnTypeIsRefReadOnly;
bool IMethod.ThisIsRefReadOnly => methodDefinition.ThisIsRefReadOnly;
public IReadOnlyList<ITypeParameter> TypeParameters {
get {
return specializedTypeParameters ?? methodDefinition.TypeParameters;

1
ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs

@ -114,6 +114,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -114,6 +114,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
IEnumerable<IAttribute> IEntity.GetAttributes() => baseMethod.GetAttributes();
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes();
bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly;
bool IMethod.ThisIsRefReadOnly => baseMethod.ThisIsRefReadOnly;
public IReadOnlyList<ITypeParameter> TypeParameters {
get { return baseMethod.TypeParameters; }

41
ILSpy.AddIn/AssemblyFileFinder.cs

@ -15,35 +15,13 @@ namespace ICSharpCode.ILSpy.AddIn @@ -15,35 +15,13 @@ namespace ICSharpCode.ILSpy.AddIn
{
public static string FindAssemblyFile(AssemblyDefinition assemblyDefinition, string assemblyFile)
{
var assemblyName = assemblyDefinition.Name;
var detectedTargetFramework = DetectTargetFrameworkId(assemblyDefinition, assemblyFile);
if (string.IsNullOrEmpty(detectedTargetFramework)) {
// Without a target framework id it makes no sense to continue
return null;
}
var targetFramework = detectedTargetFramework.Split(new[] { ",Version=v" }, StringSplitOptions.None);
string file = null;
switch (targetFramework[0]) {
case ".NETCoreApp":
case ".NETStandard":
if (targetFramework.Length != 2)
return FindAssemblyFromGAC(assemblyDefinition);
var version = targetFramework[1].Length == 3 ? new Version(targetFramework[1] + ".0") : new Version(targetFramework[1]);
var dotNetCorePathFinder = new DotNetCorePathFinder(assemblyFile, detectedTargetFramework, version);
file = dotNetCorePathFinder.TryResolveDotNetCore(Decompiler.Metadata.AssemblyNameReference.Parse(assemblyName.FullName));
if (file != null)
return file;
return FindAssemblyFromGAC(assemblyDefinition);
default:
return FindAssemblyFromGAC(assemblyDefinition);
var assemblyResolver = new UniversalAssemblyResolver(assemblyFile, false,
DetectTargetFrameworkId(assemblyDefinition, assemblyFile));
if (IsReferenceAssembly(assemblyDefinition, assemblyFile)) {
assemblyResolver.RemoveSearchDirectory(Path.GetDirectoryName(assemblyFile));
}
}
static string FindAssemblyFromGAC(AssemblyDefinition assemblyDefinition)
{
return GacInterop.FindAssemblyInNetGac(Decompiler.Metadata.AssemblyNameReference.Parse(assemblyDefinition.Name.FullName));
return assemblyResolver.FindAssemblyFile(
ICSharpCode.Decompiler.Metadata.AssemblyNameReference.Parse(assemblyDefinition.Name.FullName));
}
static readonly string RefPathPattern = @"NuGetFallbackFolder[/\\][^/\\]+[/\\][^/\\]+[/\\]ref[/\\]";
@ -60,7 +38,7 @@ namespace ICSharpCode.ILSpy.AddIn @@ -60,7 +38,7 @@ namespace ICSharpCode.ILSpy.AddIn
static readonly string DetectTargetFrameworkIdRefPathPattern =
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<1>.NETFramework)[/\\]v(?<2>[^/\\]+)[/\\])" +
@"|(NuGetFallbackFolder[/\\](?<1>[^/\\]+)\\(?<2>[^/\\]+)([/\\].*)?[/\\]ref[/\\])";
@"|((NuGetFallbackFolder|packs)[/\\](?<1>[^/\\]+)\\(?<2>[^/\\]+)([/\\].*)?[/\\]ref[/\\])";
public static string DetectTargetFrameworkId(AssemblyDefinition assembly, string assemblyPath = null)
{
@ -85,6 +63,7 @@ namespace ICSharpCode.ILSpy.AddIn @@ -85,6 +63,7 @@ namespace ICSharpCode.ILSpy.AddIn
*
* - .NETFramework -> C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\mscorlib.dll
* - .NETCore -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Console.dll
* -> C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\3.0.0\ref\netcoreapp3.0\System.Runtime.Extensions.dll
* - .NETStandard -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll
*/
var pathMatch = Regex.Match(assemblyPath, DetectTargetFrameworkIdRefPathPattern,
@ -95,9 +74,9 @@ namespace ICSharpCode.ILSpy.AddIn @@ -95,9 +74,9 @@ namespace ICSharpCode.ILSpy.AddIn
if (type == ".NETFramework") {
return $".NETFramework,Version=v{version}";
} else if (type.Contains("netcore")) {
} else if (type.ToLower().Contains("netcore")) {
return $".NETCoreApp,Version=v{version}";
} else if (type.Contains("netstandard")) {
} else if (type.ToLower().Contains("netstandard")) {
return $".NETStandard,Version=v{version}";
}
}

5
ILSpy.AddIn/Commands/NuGetReferenceForILSpy.cs

@ -4,6 +4,7 @@ using System.Linq; @@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
namespace ICSharpCode.ILSpy.AddIn.Commands
{
@ -26,6 +27,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -26,6 +27,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
/// <returns><see cref="NuGetReferenceForILSpy"/> instance or <c>null</c>, if item is not a supported project.</returns>
public static NuGetReferenceForILSpy Detect(object itemData)
{
ThreadHelper.ThrowIfNotOnUIThread();
if (itemData is ProjectItem projectItem) {
var properties = Utils.GetProperties(projectItem.Properties, "Type");
if ((properties[0] as string) == "Package") {
@ -42,6 +45,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -42,6 +45,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
/// <returns>Parameters object or <c>null, if not applicable.</c></returns>
public ILSpyParameters GetILSpyParameters()
{
ThreadHelper.ThrowIfNotOnUIThread();
var properties = Utils.GetProperties(projectItem.Properties, "Name", "Version", "Path");
if (properties[0] != null && properties[1] != null && properties[2] != null) {
return new ILSpyParameters(new[] { $"{properties[2]}\\{properties[0]}.{properties[1]}.nupkg" });

13
ILSpy.AddIn/Commands/OpenCodeItemCommand.cs

@ -23,6 +23,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -23,6 +23,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
protected override void OnBeforeQueryStatus(object sender, EventArgs e)
{
ThreadHelper.ThrowIfNotOnUIThread();
if (sender is OleMenuCommand menuItem) {
menuItem.Visible = false;
@ -46,6 +48,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -46,6 +48,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
Document GetRoslynDocument()
{
ThreadHelper.ThrowIfNotOnUIThread();
var document = owner.DTE.ActiveDocument;
var selection = (EnvDTE.TextPoint)((EnvDTE.TextSelection)document.Selection).ActivePoint;
var id = owner.Workspace.CurrentSolution.GetDocumentIdsWithFilePath(document.FullName).FirstOrDefault();
@ -57,18 +61,26 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -57,18 +61,26 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
EnvDTE.TextPoint GetEditorSelection()
{
ThreadHelper.ThrowIfNotOnUIThread();
var document = owner.DTE.ActiveDocument;
return ((EnvDTE.TextSelection)document.Selection).ActivePoint;
}
protected override async void OnExecute(object sender, EventArgs e)
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
var textView = Utils.GetCurrentViewHost(owner)?.TextView;
if (textView == null)
return;
SnapshotPoint caretPosition = textView.Caret.Position.BufferPosition;
var roslynDocument = GetRoslynDocument();
if (roslynDocument == null) {
owner.ShowMessage("This element is not analyzable in current view.");
return;
}
var ast = await roslynDocument.GetSyntaxRootAsync().ConfigureAwait(false);
var model = await roslynDocument.GetSemanticModelAsync().ConfigureAwait(false);
var node = ast.FindNode(new TextSpan(caretPosition.Position, 0), false, true);
@ -89,6 +101,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -89,6 +101,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
// Add our own project as well (not among references)
var project = FindProject(owner.DTE.Solution.Projects.OfType<EnvDTE.Project>(), roslynProject.FilePath);
if (project == null) {
owner.ShowMessage(OLEMSGICON.OLEMSGICON_WARNING, "Can't show ILSpy for this code element!");
return;

46
ILSpy.AddIn/Commands/OpenILSpyCommand.cs

@ -55,6 +55,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -55,6 +55,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
protected void OpenAssembliesInILSpy(ILSpyParameters parameters)
{
ThreadHelper.ThrowIfNotOnUIThread();
if (parameters == null)
return;
@ -71,18 +73,15 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -71,18 +73,15 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
protected Dictionary<string, DetectedReference> GetReferences(Microsoft.CodeAnalysis.Project parentProject)
{
ThreadHelper.ThrowIfNotOnUIThread();
var dict = new Dictionary<string, DetectedReference>();
foreach (var reference in parentProject.MetadataReferences) {
using (var assemblyDef = AssemblyDefinition.ReadAssembly(reference.Display)) {
string assemblyName = assemblyDef.Name.Name;
if (AssemblyFileFinder.IsReferenceAssembly(assemblyDef, reference.Display)) {
string resolvedAssemblyFile = AssemblyFileFinder.FindAssemblyFile(assemblyDef, reference.Display);
dict.Add(assemblyName,
new DetectedReference(assemblyName, resolvedAssemblyFile, false));
} else {
dict.Add(assemblyName,
new DetectedReference(assemblyName, reference.Display, false));
}
string resolvedAssemblyFile = AssemblyFileFinder.FindAssemblyFile(assemblyDef, reference.Display);
dict.Add(assemblyName,
new DetectedReference(assemblyName, resolvedAssemblyFile, false));
}
}
foreach (var projectReference in parentProject.ProjectReferences) {
@ -97,17 +96,28 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -97,17 +96,28 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
protected EnvDTE.Project FindProject(IEnumerable<EnvDTE.Project> projects, string projectFile)
{
ThreadHelper.ThrowIfNotOnUIThread();
foreach (var project in projects) {
if (project.Kind == DTEConstants.vsProjectKindSolutionItems) {
// This is a solution folder -> search in sub-projects
var subProject = FindProject(
project.ProjectItems.OfType<EnvDTE.ProjectItem>().Select(pi => pi.SubProject).OfType<EnvDTE.Project>(),
projectFile);
if (subProject != null)
return subProject;
} else {
if (project.FileName == projectFile)
return project;
switch (project.Kind) {
case DTEConstants.vsProjectKindSolutionItems:
// This is a solution folder -> search in sub-projects
var subProject = FindProject(
project.ProjectItems.OfType<EnvDTE.ProjectItem>().Select(pi => pi.SubProject).OfType<EnvDTE.Project>(),
projectFile);
if (subProject != null)
return subProject;
break;
case DTEConstants.vsProjectKindUnmodeled:
// Skip unloaded projects completely
break;
default:
// Match by project's file name
if (project.FileName == projectFile)
return project;
break;
}
}

4
ILSpy.AddIn/Commands/OpenProjectOutputCommand.cs

@ -17,6 +17,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -17,6 +17,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
protected override void OnBeforeQueryStatus(object sender, EventArgs e)
{
ThreadHelper.ThrowIfNotOnUIThread();
if (sender is OleMenuCommand menuItem) {
menuItem.Visible = false;
@ -27,6 +29,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -27,6 +29,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
protected override void OnExecute(object sender, EventArgs e)
{
ThreadHelper.ThrowIfNotOnUIThread();
if (owner.DTE.SelectedItems.Count != 1)
return;
var projectItemWrapper = ProjectItemForILSpy.Detect(owner, owner.DTE.SelectedItems.Item(1));

4
ILSpy.AddIn/Commands/OpenReferenceCommand.cs

@ -22,6 +22,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -22,6 +22,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
protected override void OnBeforeQueryStatus(object sender, EventArgs e)
{
ThreadHelper.ThrowIfNotOnUIThread();
if (sender is OleMenuCommand menuItem) {
menuItem.Visible = false;
@ -44,6 +46,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -44,6 +46,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
protected override void OnExecute(object sender, EventArgs e)
{
ThreadHelper.ThrowIfNotOnUIThread();
var itemObject = owner.GetSelectedItemsData<object>().FirstOrDefault();
if (itemObject == null)
return;

5
ILSpy.AddIn/Commands/ProjectItemForILSpy.cs

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
using System.IO;
using System.Linq;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
namespace ICSharpCode.ILSpy.AddIn.Commands
{
@ -27,6 +28,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -27,6 +28,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
/// <returns><see cref="ProjectItemForILSpy"/> instance or <c>null</c>, if item is not a supported project.</returns>
public static ProjectItemForILSpy Detect(ILSpyAddInPackage package, SelectedItem item)
{
ThreadHelper.ThrowIfNotOnUIThread();
var project = item.Project;
var roslynProject = package.Workspace.CurrentSolution.Projects.FirstOrDefault(p => p.FilePath == project.FileName);
if (roslynProject == null)
@ -42,6 +45,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -42,6 +45,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
/// <returns>Parameters object or <c>null, if not applicable.</c></returns>
public ILSpyParameters GetILSpyParameters(ILSpyAddInPackage package)
{
ThreadHelper.ThrowIfNotOnUIThread();
return new ILSpyParameters(new[] { Utils.GetProjectOutputAssembly(project, roslynProject) });
}
}

5
ILSpy.AddIn/Commands/ProjectReferenceForILSpy.cs

@ -4,6 +4,7 @@ using System.Linq; @@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.AddIn.Commands
@ -31,6 +32,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -31,6 +32,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
/// <returns><see cref="ProjectReferenceForILSpy"/> instance or <c>null</c>, if item is not a supported project.</returns>
public static ProjectReferenceForILSpy Detect(object itemData)
{
ThreadHelper.ThrowIfNotOnUIThread();
if (itemData is ProjectItem projectItem) {
var properties = Utils.GetProperties(projectItem.Properties, "FusionName", "ResolvedPath");
string fusionName = properties[0] as string;
@ -50,6 +53,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -50,6 +53,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
/// <returns>Parameters object or <c>null, if not applicable.</c></returns>
public ILSpyParameters GetILSpyParameters(Dictionary<string, DetectedReference> projectReferences)
{
ThreadHelper.ThrowIfNotOnUIThread();
string fileName = projectItem.ContainingProject?.FileName;
if (!string.IsNullOrEmpty(fileName)) {
if (projectReferences.TryGetValue(projectItem.Name, out DetectedReference path)) {

17
ILSpy.AddIn/ILSpy.AddIn.csproj

@ -44,27 +44,15 @@ @@ -44,27 +44,15 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="EnvDTE" Version="8.0.2" />
<PackageReference Include="EnvDTE80" Version="8.0.2" />
<PackageReference Include="Microsoft.VisualStudio.SDK " Version="15.9.3" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="2.4.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.4.0" />
<PackageReference Include="Microsoft.VisualStudio.ComponentModelHost" Version="15.6.27413" />
<PackageReference Include="Microsoft.VisualStudio.Editor" Version="15.6.27740" />
<PackageReference Include="Microsoft.VisualStudio.LanguageServices" Version="2.4.0" />
<PackageReference Include="Microsoft.VisualStudio.SDK.Analyzers" Version="16.0.29" />
<PackageReference Include="Microsoft.VisualStudio.Shell.15.0" Version="15.6.27413" />
<PackageReference Include="Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime" Version="14.3.26929" />
<PackageReference Include="Microsoft.VisualStudio.Text.UI" Version="15.6.27740" />
<PackageReference Include="Microsoft.VisualStudio.Text.UI.Wpf" Version="15.6.27740" />
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="15.6.56" />
<PackageReference Include="Mono.Cecil" Version="0.10.3" />
<PackageReference Include="VSLangProj" Version="7.0.3301" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj" />
<ProjectReference Include="..\ILSpy\ILSpy.csproj" />
<ProjectReference Include="..\SharpTreeView\ICSharpCode.TreeView.csproj" />
</ItemGroup>
<ItemGroup>
@ -129,8 +117,9 @@ @@ -129,8 +117,9 @@
<AdditionalDependencies Include="$(ILSpyBuildPath)Mono.Cecil.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Mono.Cecil.Pdb.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)ILSpy.BamlDecompiler.Plugin.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Microsoft.DiaSymReader*.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Microsoft.DiaSymReader*.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)OSVersionHelper.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Xceed.Wpf.AvalonDock.dll" />
</ItemGroup>
<ItemGroup>

6
ILSpy.AddIn/ILSpyAddInPackage.cs

@ -113,6 +113,10 @@ namespace ICSharpCode.ILSpy.AddIn @@ -113,6 +113,10 @@ namespace ICSharpCode.ILSpy.AddIn
ThreadHelper.ThrowIfNotOnUIThread();
IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));
if (uiShell == null) {
return 0;
}
Guid clsid = Guid.Empty;
int result;
Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(
@ -136,6 +140,8 @@ namespace ICSharpCode.ILSpy.AddIn @@ -136,6 +140,8 @@ namespace ICSharpCode.ILSpy.AddIn
public IEnumerable<T> GetSelectedItemsData<T>()
{
ThreadHelper.ThrowIfNotOnUIThread();
if (DTE.ToolWindows.SolutionExplorer.SelectedItems is IEnumerable<UIHierarchyItem> hierarchyItems) {
foreach (var item in hierarchyItems) {
if (item.Object is T typedItem) {

4
ILSpy.AddIn/ILSpyInstance.cs

@ -42,7 +42,8 @@ namespace ICSharpCode.ILSpy.AddIn @@ -42,7 +42,8 @@ namespace ICSharpCode.ILSpy.AddIn
var process = new Process() {
StartInfo = new ProcessStartInfo() {
FileName = GetILSpyPath(),
UseShellExecute = false
UseShellExecute = false,
Arguments = "/navigateTo:none"
}
};
process.Start();
@ -53,6 +54,7 @@ namespace ICSharpCode.ILSpy.AddIn @@ -53,6 +54,7 @@ namespace ICSharpCode.ILSpy.AddIn
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD110:Observe result of async calls", Justification = "<Pending>")]
void SendMessage(Process ilspyProcess, string message, bool activate)
{
// We wait asynchronously until target window can be found and try to find it multiple times

3
ILSpy.AddIn/README.md

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
Test cases for various types of projects as well as corresponding test plans are located in a separate repository.
[https://github.com/icsharpcode/ILSpy-Addin-tests](https://github.com/icsharpcode/ILSpy-Addin-tests)

11
ILSpy.AddIn/Utils.cs

@ -8,6 +8,7 @@ using System.Text; @@ -8,6 +8,7 @@ using System.Text;
using EnvDTE;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.LanguageServices;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.TextManager.Interop;
@ -148,6 +149,8 @@ namespace ICSharpCode.ILSpy.AddIn @@ -148,6 +149,8 @@ namespace ICSharpCode.ILSpy.AddIn
public static object[] GetProperties(EnvDTE.Properties properties, params string[] names)
{
ThreadHelper.ThrowIfNotOnUIThread();
var values = new object[names.Length];
foreach (object p in properties) {
try {
@ -168,6 +171,8 @@ namespace ICSharpCode.ILSpy.AddIn @@ -168,6 +171,8 @@ namespace ICSharpCode.ILSpy.AddIn
public static List<(string, object)> GetAllProperties(EnvDTE.Properties properties)
{
ThreadHelper.ThrowIfNotOnUIThread();
var result = new List<(string, object)>();
for (int i = 0; i < properties.Count; i++) {
try {
@ -207,6 +212,10 @@ namespace ICSharpCode.ILSpy.AddIn @@ -207,6 +212,10 @@ namespace ICSharpCode.ILSpy.AddIn
public static IWpfTextViewHost GetCurrentViewHost(IServiceProvider serviceProvider)
{
IVsTextManager txtMgr = (IVsTextManager)serviceProvider.GetService(typeof(SVsTextManager));
if (txtMgr == null) {
return null;
}
IVsTextView vTextView = null;
int mustHaveFocus = 1;
txtMgr.GetActiveView(mustHaveFocus, null, out vTextView);
@ -235,6 +244,8 @@ namespace ICSharpCode.ILSpy.AddIn @@ -235,6 +244,8 @@ namespace ICSharpCode.ILSpy.AddIn
public static string GetProjectOutputAssembly(Project project, Microsoft.CodeAnalysis.Project roslynProject)
{
ThreadHelper.ThrowIfNotOnUIThread();
string outputFileName = Path.GetFileName(roslynProject.OutputFilePath);
// Get the directory path based on the project file.

4
ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj

@ -32,12 +32,12 @@ @@ -32,12 +32,12 @@
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AvalonEdit" Version="6.0.0-rc1" />
<PackageReference Include="AvalonEdit" Version="6.0.0" />
<PackageReference Include="System.Composition" Version="1.3.0" />
</ItemGroup>
<ItemGroup>

4
ILSpy.Tests/ILSpy.Tests.csproj

@ -42,8 +42,8 @@ @@ -42,8 +42,8 @@
<ItemGroup>
<PackageReference Include="DiffLib" Version="2017.7.26.1241" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.0.0-beta4-final" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="3.0.0-beta4-final" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.3.1" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic" Version="3.3.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.13.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
<PackageReference Include="NUnit" Version="3.11.0" />

117
ILSpy/AboutPage.cs

@ -19,7 +19,6 @@ @@ -19,7 +19,6 @@
using System;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
@ -129,20 +128,19 @@ namespace ICSharpCode.ILSpy @@ -129,20 +128,19 @@ namespace ICSharpCode.ILSpy
button.Cursor = Cursors.Arrow;
stackPanel.Children.Add(button);
button.Click += delegate {
button.Click += async delegate {
button.Content = Resources.Checking;
button.IsEnabled = false;
GetLatestVersionAsync().ContinueWith(
delegate (Task<AvailableVersionInfo> task) {
try {
stackPanel.Children.Clear();
ShowAvailableVersion(task.Result, stackPanel);
} catch (Exception ex) {
AvalonEditTextOutput exceptionOutput = new AvalonEditTextOutput();
exceptionOutput.WriteLine(ex.ToString());
textView.ShowText(exceptionOutput);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
try {
AvailableVersionInfo vInfo = await GetLatestVersionAsync();
stackPanel.Children.Clear();
ShowAvailableVersion(vInfo, stackPanel);
} catch (Exception ex) {
AvalonEditTextOutput exceptionOutput = new AvalonEditTextOutput();
exceptionOutput.WriteLine(ex.ToString());
textView.ShowText(exceptionOutput);
}
};
}
@ -183,36 +181,25 @@ namespace ICSharpCode.ILSpy @@ -183,36 +181,25 @@ namespace ICSharpCode.ILSpy
}
}
static Task<AvailableVersionInfo> GetLatestVersionAsync()
static async Task<AvailableVersionInfo> GetLatestVersionAsync()
{
var tcs = new TaskCompletionSource<AvailableVersionInfo>();
new Action(() => {
WebClient wc = new WebClient();
IWebProxy systemWebProxy = WebRequest.GetSystemWebProxy();
systemWebProxy.Credentials = CredentialCache.DefaultCredentials;
wc.Proxy = systemWebProxy;
wc.DownloadDataCompleted += delegate(object sender, DownloadDataCompletedEventArgs e) {
if (e.Error != null) {
tcs.SetException(e.Error);
} else {
try {
XDocument doc = XDocument.Load(new MemoryStream(e.Result));
var bands = doc.Root.Elements("band");
var currentBand = bands.FirstOrDefault(b => (string)b.Attribute("id") == band) ?? bands.First();
Version version = new Version((string)currentBand.Element("latestVersion"));
string url = (string)currentBand.Element("downloadUrl");
if (!(url.StartsWith("http://", StringComparison.Ordinal) || url.StartsWith("https://", StringComparison.Ordinal)))
url = null; // don't accept non-urls
latestAvailableVersion = new AvailableVersionInfo { Version = version, DownloadUrl = url };
tcs.SetResult(latestAvailableVersion);
} catch (Exception ex) {
tcs.SetException(ex);
}
}
};
wc.DownloadDataAsync(UpdateUrl);
}).BeginInvoke(null, null);
return tcs.Task;
WebClient wc = new WebClient();
IWebProxy systemWebProxy = WebRequest.GetSystemWebProxy();
systemWebProxy.Credentials = CredentialCache.DefaultCredentials;
wc.Proxy = systemWebProxy;
string data = await wc.DownloadStringTaskAsync(UpdateUrl);
XDocument doc = XDocument.Load(new StringReader(data));
var bands = doc.Root.Elements("band");
var currentBand = bands.FirstOrDefault(b => (string)b.Attribute("id") == band) ?? bands.First();
Version version = new Version((string)currentBand.Element("latestVersion"));
string url = (string)currentBand.Element("downloadUrl");
if (!(url.StartsWith("http://", StringComparison.Ordinal) || url.StartsWith("https://", StringComparison.Ordinal)))
url = null; // don't accept non-urls
latestAvailableVersion = new AvailableVersionInfo { Version = version, DownloadUrl = url };
return latestAvailableVersion;
}
sealed class AvailableVersionInfo
@ -274,9 +261,7 @@ namespace ICSharpCode.ILSpy @@ -274,9 +261,7 @@ namespace ICSharpCode.ILSpy
void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
@ -285,10 +270,10 @@ namespace ICSharpCode.ILSpy @@ -285,10 +270,10 @@ namespace ICSharpCode.ILSpy
/// Returns the download URL if an update is available.
/// Returns null if no update is available, or if no check was performed.
/// </summary>
public static Task<string> CheckForUpdatesIfEnabledAsync(ILSpySettings spySettings)
public static async Task<string> CheckForUpdatesIfEnabledAsync(ILSpySettings spySettings)
{
var tcs = new TaskCompletionSource<string>();
UpdateSettings s = new UpdateSettings(spySettings);
// If we're in an MSIX package, updates work differently
if (s.AutomaticUpdateCheckEnabled && !WindowsVersionHelper.HasPackageIdentity) {
// perform update check if we never did one before;
@ -297,40 +282,34 @@ namespace ICSharpCode.ILSpy @@ -297,40 +282,34 @@ namespace ICSharpCode.ILSpy
|| s.LastSuccessfulUpdateCheck < DateTime.UtcNow.AddDays(-7)
|| s.LastSuccessfulUpdateCheck > DateTime.UtcNow)
{
CheckForUpdateInternal(tcs, s);
return await CheckForUpdateInternal(s);
} else {
tcs.SetResult(null);
return null;
}
} else {
tcs.SetResult(null);
return null;
}
return tcs.Task;
}
public static Task<string> CheckForUpdatesAsync(ILSpySettings spySettings)
{
var tcs = new TaskCompletionSource<string>();
UpdateSettings s = new UpdateSettings(spySettings);
CheckForUpdateInternal(tcs, s);
return tcs.Task;
return CheckForUpdateInternal(s);
}
static void CheckForUpdateInternal(TaskCompletionSource<string> tcs, UpdateSettings s)
static async Task<string> CheckForUpdateInternal(UpdateSettings s)
{
GetLatestVersionAsync().ContinueWith(
delegate (Task<AvailableVersionInfo> task) {
try {
s.LastSuccessfulUpdateCheck = DateTime.UtcNow;
AvailableVersionInfo v = task.Result;
if (v.Version > currentVersion)
tcs.SetResult(v.DownloadUrl);
else
tcs.SetResult(null);
} catch (AggregateException) {
// ignore errors getting the version info
tcs.SetResult(null);
}
});
try {
var v = await GetLatestVersionAsync();
s.LastSuccessfulUpdateCheck = DateTime.UtcNow;
if (v.Version > currentVersion)
return v.DownloadUrl;
else
return null;
} catch (Exception) {
// ignore errors getting the version info
return null;
}
}
}

2
ILSpy/Analyzers/AnalyzerTreeView.cs

@ -73,7 +73,7 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -73,7 +73,7 @@ namespace ICSharpCode.ILSpy.Analyzers
public void Show()
{
if (!IsVisible)
MainWindow.Instance.ShowInBottomPane("Analyzer", this);
MainWindow.Instance.ShowInNewPane("Analyzer", this, PanePosition.Bottom);
}
public void Show(AnalyzerTreeNode node)

2
ILSpy/Analyzers/RemoveAnalyzeContextMenuEntry.cs

@ -20,7 +20,7 @@ using System.Linq; @@ -20,7 +20,7 @@ using System.Linq;
namespace ICSharpCode.ILSpy.Analyzers
{
[ExportContextMenuEntry(Header = "Remove", Icon = "images/Delete.png", Category = "Analyze", Order = 200)]
[ExportContextMenuEntry(Header = "Remove", Icon = "images/Delete", Category = "Analyze", Order = 200)]
internal sealed class RemoveAnalyzeContextMenuEntry : IContextMenuEntry
{
public bool IsVisible(TextViewContext context)

4
ILSpy/App.xaml.cs

@ -94,7 +94,7 @@ namespace ICSharpCode.ILSpy @@ -94,7 +94,7 @@ namespace ICSharpCode.ILSpy
var name = Path.GetFileNameWithoutExtension(plugin);
try {
var asm = Assembly.Load(name);
var parts = discovery.CreatePartsAsync(asm).Result;
var parts = discovery.CreatePartsAsync(asm).GetAwaiter().GetResult();
catalog = catalog.AddParts(parts);
} catch (Exception ex) {
StartupExceptions.Add(new ExceptionData { Exception = ex, PluginName = name });
@ -102,7 +102,7 @@ namespace ICSharpCode.ILSpy @@ -102,7 +102,7 @@ namespace ICSharpCode.ILSpy
}
}
// Add the built-in parts
catalog = catalog.AddParts(discovery.CreatePartsAsync(Assembly.GetExecutingAssembly()).Result);
catalog = catalog.AddParts(discovery.CreatePartsAsync(Assembly.GetExecutingAssembly()).GetAwaiter().GetResult());
// If/When the project switches to .NET Standard/Core, this will be needed to allow metadata interfaces (as opposed
// to metadata classes). When running on .NET Framework, it's automatic.
// catalog.WithDesktopSupport();

4
ILSpy/Commands/CheckForUpdatesCommand.cs

@ -34,9 +34,9 @@ namespace ICSharpCode.ILSpy @@ -34,9 +34,9 @@ namespace ICSharpCode.ILSpy
return base.CanExecute(parameter);
}
public override void Execute(object parameter)
public override async void Execute(object parameter)
{
MainWindow.Instance.ShowMessageIfUpdatesAvailableAsync(ILSpySettings.Load(), forceCheck: true);
await MainWindow.Instance.ShowMessageIfUpdatesAvailableAsync(ILSpySettings.Load(), forceCheck: true);
}
}
}

52
ILSpy/Commands/DecompileInNewViewCommand.cs

@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// 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.Linq;
using ICSharpCode.ILSpy.Properties;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
namespace ICSharpCode.ILSpy.Commands
{
// [ExportContextMenuEntry(Header = nameof(Resources.DecompileToNewPanel), Icon = "images/Search", Category = nameof(Resources.Analyze), Order = 90)]
internal sealed class DecompileInNewViewCommand : IContextMenuEntry
{
public bool IsVisible(TextViewContext context)
{
if (context.SelectedTreeNodes == null)
return false;
return true;
}
public bool IsEnabled(TextViewContext context)
{
if (context.SelectedTreeNodes == null)
return false;
return true;
}
public async void Execute(TextViewContext context)
{
var dtv = new DecompilerTextView();
var nodes = context.SelectedTreeNodes.Cast<ILSpyTreeNode>().ToArray();
var title = string.Join(", ", nodes.Select(x => x.ToString()));
MainWindow.Instance.ShowInNewPane(title, dtv, PanePosition.Document);
await dtv.DecompileAsync(MainWindow.Instance.CurrentLanguage, nodes, new DecompilationOptions());
}
}
}

16
ILSpy/ContextMenuEntry.cs

@ -78,7 +78,7 @@ namespace ICSharpCode.ILSpy @@ -78,7 +78,7 @@ namespace ICSharpCode.ILSpy
if (textView != null)
reference = textView.GetReferenceSegmentAtMousePosition();
else if (listBox?.SelectedItem is SearchResult result)
reference = new ReferenceSegment { Reference = result.Member };
reference = new ReferenceSegment { Reference = result.Reference };
else
reference = null;
var position = textView != null ? textView.GetPositionFromMousePosition() : null;
@ -222,15 +222,11 @@ namespace ICSharpCode.ILSpy @@ -222,15 +222,11 @@ namespace ICSharpCode.ILSpy
menuItem.Header = MainWindow.GetResourceString( entryPair.Metadata.Header);
menuItem.InputGestureText = entryPair.Metadata.InputGestureText;
if (!string.IsNullOrEmpty(entryPair.Metadata.Icon)) {
object image = Images.Load(entryPair.Value, entryPair.Metadata.Icon);
if (!(image is Viewbox)) {
image = new Image {
Width = 16,
Height = 16,
Source = (ImageSource)image
};
}
menuItem.Icon = image;
menuItem.Icon = new Image {
Width = 16,
Height = 16,
Source = Images.Load(entryPair.Value, entryPair.Metadata.Icon)
};
}
if (entryPair.Value.IsEnabled(context)) {
menuItem.Click += delegate { entry.Execute(context); };

76
ILSpy/Controls/DockedPane.cs

@ -1,76 +0,0 @@ @@ -1,76 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// 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.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace ICSharpCode.ILSpy.Controls
{
class DockedPane : Control
{
static DockedPane()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DockedPane), new FrameworkPropertyMetadata(typeof(DockedPane)));
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(DockedPane));
public string Title {
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(object), typeof(DockedPane));
public object Content {
get { return GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Button closeButton = (Button)this.Template.FindName("PART_Close", this);
if (closeButton != null) {
closeButton.Click += closeButton_Click;
}
}
void closeButton_Click(object sender, RoutedEventArgs e)
{
if (CloseButtonClicked != null)
CloseButtonClicked(this, e);
}
public event EventHandler CloseButtonClicked;
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.Key == Key.F4 && e.KeyboardDevice.Modifiers == ModifierKeys.Control || e.Key == Key.Escape) {
if (CloseButtonClicked != null)
CloseButtonClicked(this, e);
e.Handled = true;
}
}
}
}

2
ILSpy/DebugSteps.xaml.cs

@ -79,7 +79,7 @@ namespace ICSharpCode.ILSpy @@ -79,7 +79,7 @@ namespace ICSharpCode.ILSpy
public static void Show()
{
MainWindow.Instance.ShowInTopPane(Properties.Resources.DebugSteps, new DebugSteps());
MainWindow.Instance.ShowInNewPane(Properties.Resources.DebugSteps, new DebugSteps(), PanePosition.Top);
}
void IPane.Closed()

50
ILSpy/Docking/DockLayoutSettings.cs

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
using System;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using Xceed.Wpf.AvalonDock.Layout.Serialization;
namespace ICSharpCode.ILSpy.Docking
{
public class DockLayoutSettings
{
private string rawSettings;
public bool Valid => rawSettings != null;
public DockLayoutSettings(XElement element)
{
if ((element != null) && element.HasElements) {
rawSettings = element.Elements().FirstOrDefault()?.ToString();
}
}
public XElement SaveAsXml()
{
try {
return XElement.Parse(rawSettings);
} catch (Exception) {
return null;
}
}
public void Deserialize(XmlLayoutSerializer serializer)
{
if (!Valid)
return;
using (StringReader reader = new StringReader(rawSettings)) {
serializer.Deserialize(reader);
}
}
public void Serialize(XmlLayoutSerializer serializer)
{
using (StringWriter fs = new StringWriter()) {
serializer.Serialize(fs);
rawSettings = fs.ToString();
}
}
}
}

32
ILSpy/Docking/DockingHelper.cs

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
using System.Windows;
using System.Windows.Controls;
using Xceed.Wpf.AvalonDock.Layout;
namespace ICSharpCode.ILSpy.Docking
{
public static class DockingHelper
{
public static void DockHorizontal(LayoutContent layoutContent, ILayoutElement paneRelativeTo, GridLength dockHeight, bool dockBefore = false)
{
if (paneRelativeTo is ILayoutDocumentPane parentDocumentPane) {
var parentDocumentGroup = paneRelativeTo.FindParent<LayoutDocumentPaneGroup>();
if (parentDocumentGroup == null) {
var grandParent = parentDocumentPane.Parent as ILayoutContainer;
parentDocumentGroup = new LayoutDocumentPaneGroup() { Orientation = System.Windows.Controls.Orientation.Vertical };
grandParent.ReplaceChild(paneRelativeTo, parentDocumentGroup);
parentDocumentGroup.Children.Add(parentDocumentPane);
}
parentDocumentGroup.Orientation = System.Windows.Controls.Orientation.Vertical;
int indexOfParentPane = parentDocumentGroup.IndexOfChild(parentDocumentPane);
var layoutDocumentPane = new LayoutDocumentPane(layoutContent) { DockHeight = dockHeight };
parentDocumentGroup.InsertChildAt(dockBefore ? indexOfParentPane : indexOfParentPane + 1, layoutDocumentPane);
layoutContent.IsActive = true;
layoutContent.Root.CollectGarbage();
Application.Current.MainWindow.Dispatcher.Invoke(() => {
layoutDocumentPane.DockHeight = dockHeight;
}, System.Windows.Threading.DispatcherPriority.Loaded);
}
}
}
}

9
ILSpy/Docking/PanePosition.cs

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
namespace ICSharpCode.ILSpy
{
public enum PanePosition
{
Top,
Bottom,
Document
}
}

15
ILSpy/ILSpy.csproj

@ -49,8 +49,10 @@ @@ -49,8 +49,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AvalonEdit" Version="6.0.0-rc1" />
<PackageReference Include="Microsoft.VisualStudio.Composition" Version="15.5.23" />
<PackageReference Include="AvalonEdit" Version="6.0.0" />
<PackageReference Include="Dirkster.AvalonDock" Version="3.5.12" />
<PackageReference Include="Microsoft.VisualStudio.Composition" Version="16.3.7" />
<PackageReference Include="System.Composition" Version="1.3.0" />
<PackageReference Include="Mono.Cecil" Version="0.10.3" />
<PackageReference Include="OSVersionHelper" Version="1.0.11" />
</ItemGroup>
@ -100,6 +102,7 @@ @@ -100,6 +102,7 @@
<Compile Include="AssemblyListManager.cs" />
<Compile Include="AvalonEdit\ITextMarker.cs" />
<Compile Include="AvalonEdit\TextMarkerService.cs" />
<Compile Include="Commands\DecompileInNewViewCommand.cs" />
<Compile Include="Commands\CheckForUpdatesCommand.cs" />
<Compile Include="Commands\BrowseBackCommand.cs" />
<Compile Include="Commands\BrowseForwardCommand.cs" />
@ -123,7 +126,6 @@ @@ -123,7 +126,6 @@
<Compile Include="Controls\ResourceObjectTable.xaml.cs">
<DependentUpon>ResourceObjectTable.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\DockedPane.cs" />
<Compile Include="Commands\DecompileAllCommand.cs" />
<Compile Include="Commands\ExportCommandAttribute.cs" />
<Compile Include="Controls\ExtensionMethods.cs" />
@ -137,6 +139,8 @@ @@ -137,6 +139,8 @@
<Compile Include="DebugSteps.xaml.cs">
<DependentUpon>DebugSteps.xaml</DependentUpon>
</Compile>
<Compile Include="Docking\DockingHelper.cs" />
<Compile Include="Docking\DockLayoutSettings.cs" />
<Compile Include="ILSpyTraceListener.cs" />
<Compile Include="DecompilationOptions.cs" />
<Compile Include="ExtensionMethods.cs" />
@ -163,11 +167,14 @@ @@ -163,11 +167,14 @@
<Compile Include="Languages\Language.cs" />
<Compile Include="Languages\Languages.cs" />
<Compile Include="Options\DecompilerSettingsPanel.xaml.cs" />
<Compile Include="Docking\PanePosition.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Search\AbstractEntitySearchStrategy.cs" />
<Compile Include="Search\AssemblySearchStrategy.cs" />
<Compile Include="Search\LiteralSearchStrategy.cs" />
<Compile Include="LoadedAssembly.cs" />
<Compile Include="LoadedAssemblyExtensions.cs" />
@ -205,11 +212,13 @@ @@ -205,11 +212,13 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Commands\RefreshCommand.cs" />
<Compile Include="Commands\SaveCommand.cs" />
<Compile Include="Search\ResourceSearchStrategy.cs" />
<Compile Include="Search\SearchPane.cs">
<DependentUpon>SearchPane.xaml</DependentUpon>
</Compile>
<Compile Include="Commands\SimpleCommand.cs" />
<Compile Include="Search\AbstractSearchStrategy.cs" />
<Compile Include="Search\SearchResult.cs" />
<Compile Include="SolutionWriter.cs" />
<Compile Include="TaskHelper.cs" />
<Compile Include="TextView\BracketHighlightRenderer.cs" />

26
ILSpy/Images/Copy.xaml

@ -1,20 +1,10 @@ @@ -1,20 +1,10 @@
<!-- This file was generated by the AiToXaml tool.-->
<!-- Tool Version: 14.0.22307.0 -->
<Viewbox Width="16" Height="16" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Rectangle Width="16" Height="16">
<Rectangle.Fill>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M0.9999,-0.000199999999999534L0.9999,13.0008 5.0009,13.0008 5.0009,15.9998 15.9999,15.9998 15.9999,7.3788 11.6209,3.0008 10.6049,3.0008 7.6179,-0.000199999999999534z" />
<GeometryDrawing Brush="#FF424242" Geometry="F1M14,14L7,14 7,5 10,5 10,9 14,9z M6,11L3,11 3,2 6.798,2 8.81,4 6,4z M11,5.207L13.793,8 11,8z M11.207,4L10.19,4 7.202,1 2,1 2,12 6,12 6,15 15,15 15,7.793z" />
<GeometryDrawing Brush="#FFF0EFF1" Geometry="F1M14,14L7,14 7,5 10,5 10,9 14,9z M6,11L3,11 3,2 6.798,2 8.81,4 6,4z M11,5.207L13.793,8 11,8z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Rectangle.Fill>
</Rectangle>
</Viewbox>
<DrawingGroup xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<DrawingGroup.Children>
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M0.9999,-0.000199999999999534L0.9999,13.0008 5.0009,13.0008 5.0009,15.9998 15.9999,15.9998 15.9999,7.3788 11.6209,3.0008 10.6049,3.0008 7.6179,-0.000199999999999534z" />
<GeometryDrawing Brush="#FF424242" Geometry="F1M14,14L7,14 7,5 10,5 10,9 14,9z M6,11L3,11 3,2 6.798,2 8.81,4 6,4z M11,5.207L13.793,8 11,8z M11.207,4L10.19,4 7.202,1 2,1 2,12 6,12 6,15 15,15 15,7.793z" />
<GeometryDrawing Brush="#FFF0EFF1" Geometry="F1M14,14L7,14 7,5 10,5 10,9 14,9z M6,11L3,11 3,2 6.798,2 8.81,4 6,4z M11,5.207L13.793,8 11,8z" />
</DrawingGroup.Children>
</DrawingGroup>

18
ILSpy/Images/README.md

@ -14,7 +14,7 @@ Icons used in ILSpy: @@ -14,7 +14,7 @@ Icons used in ILSpy:
| Constructor | x | x | based on VS 2017 Icon Pack (Method) using a different colour | |
| Copy | x | x | VS 2017 Icon Pack (Copy) | |
| Delegate | x | x | VS 2017 Icon Pack (Delegate) | |
| Delete | x | x | | |
| Delete | x | x | VS 2017 Icon Pack (Remove_color) | |
| Enum | x | x | VS 2017 Icon Pack (Enumerator) | |
| EnumValue | x | x | VS 2017 Icon Pack (EnumItem) | |
| Event | x | x | VS 2017 Icon Pack (Event) | |
@ -45,10 +45,10 @@ Icons used in ILSpy: @@ -45,10 +45,10 @@ Icons used in ILSpy:
| Property | x | x | VS 2017 Icon Pack (Property) | |
| ReferenceFolder | x | x | combined VS 2017 Icon Pack (Reference) two times | |
| Refresh | x | x | VS 2017 Icon Pack (Refresh) | |
| Resource | x | x | | |
| ResourceImage | x | x | | |
| ResourceResourcesFile | x | x | | |
| ResourceXml | x | x | | |
| Resource | x | x | VS 2017 Icon Pack (Document) | |
| ResourceImage | x | x | VS 2017 Icon Pack (Image) | |
| ResourceResourcesFile | x | x | VS 2017 Icon Pack (LocalResources) | |
| ResourceXml | x | x | VS 2017 Icon Pack (XMLFile) | |
| ResourceXsd | x | x | combined VS 2017 Icon Pack (XMLSchema) with the "file symbol in ResourceXslt | |
| ResourceXsl | x | x | VS 2017 Icon Pack (XMLTransformation) | |
| ResourceXslt | x | x | VS 2017 Icon Pack (XSLTTemplate) | |
@ -57,12 +57,12 @@ Icons used in ILSpy: @@ -57,12 +57,12 @@ Icons used in ILSpy:
| SearchMsdn | x | x | based on VS 2017 Icon Pack (Search) + Microsoft Logo | |
| ShowAll | x | x | combined PublicOnly, OverlayPrivate, OverlayProtected, OverlayInternal | |
| ShowPrivateInternal | x | x | combined OverlayPrivate and OverlayInternal | |
| ShowPublicOnly | x | x | | |
| ShowPublicOnly | x | x | VS 2017 Icon Pack (Type) | |
| Sort | x | x | VS 2017 Icon Pack (SortAscending) | |
| Struct | x | x | VS 2017 Icon Pack (Structure) | |
| SubTypes | x | x | | |
| SuperTypes | x | x | | |
| ViewCode | x | x | | |
| SubTypes | x | x | based on VS 2017 Icon Pack (BaseType) rotated +90° | |
| SuperTypes | x | x | based on VS 2017 Icon Pack (BaseType) rotated -90° | |
| ViewCode | x | x | VS 2017 Icon Pack (GoToSourceCode) | |
| VirtualMethod | x | x | combined VS 2017 Icon Pack (Method) two times | |
| Warning | x | x | VS 2017 Icon Pack (StatusWarning) | |

7
ILSpy/Languages/CSharpHighlightingTokenWriter.cs

@ -194,7 +194,6 @@ namespace ICSharpCode.ILSpy @@ -194,7 +194,6 @@ namespace ICSharpCode.ILSpy
case "event":
case "extern":
case "override":
case "readonly":
case "sealed":
case "static":
case "virtual":
@ -203,6 +202,12 @@ namespace ICSharpCode.ILSpy @@ -203,6 +202,12 @@ namespace ICSharpCode.ILSpy
case "partial":
color = modifiersColor;
break;
case "readonly":
if (role == ComposedType.ReadonlyRole)
color = parameterModifierColor;
else
color = modifiersColor;
break;
case "checked":
case "unchecked":
color = checkedKeywordColor;

2
ILSpy/LoadedAssembly.cs

@ -87,7 +87,7 @@ namespace ICSharpCode.ILSpy @@ -87,7 +87,7 @@ namespace ICSharpCode.ILSpy
public PEFile GetPEFileOrNull()
{
try {
return GetPEFileAsync().Result;
return GetPEFileAsync().GetAwaiter().GetResult();
} catch (Exception ex) {
System.Diagnostics.Trace.TraceError(ex.ToString());
return null;

137
ILSpy/MainWindow.xaml

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
x:ClassModifier="public"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:tv="clr-namespace:ICSharpCode.TreeView;assembly=ICSharpCode.TreeView"
xmlns:local="clr-namespace:ICSharpCode.ILSpy"
xmlns:textView="clr-namespace:ICSharpCode.ILSpy.TextView"
xmlns:avalondock="http://schemas.xceed.com/wpf/xaml/avalondock"
xmlns:controls="clr-namespace:ICSharpCode.ILSpy.Controls"
xmlns:properties="clr-namespace:ICSharpCode.ILSpy.Properties"
Title="ILSpy"
@ -51,7 +51,8 @@ @@ -51,7 +51,8 @@
<DockPanel>
<!-- Main menu -->
<Menu DockPanel.Dock="Top" Name="mainMenu" Height="23" KeyboardNavigation.TabNavigation="None">
<MenuItem Header="{x:Static properties:Resources._File}" /> <!-- contents of file menu are added using MEF -->
<MenuItem Header="{x:Static properties:Resources._File}" />
<!-- contents of file menu are added using MEF -->
<MenuItem Header="{x:Static properties:Resources._View}">
<MenuItem Header="{x:Static properties:Resources.Show_publiconlyTypesMembers}" IsCheckable="True" IsChecked="{Binding FilterSettings.ApiVisPublicOnly}" />
<MenuItem Header="{x:Static properties:Resources.Show_internalTypesMembers}" IsCheckable="True" IsChecked="{Binding FilterSettings.ApiVisPublicAndInternal}" />
@ -96,6 +97,15 @@ @@ -96,6 +97,15 @@
ItemsSource="{Binding SelectedItem.LanguageVersions, ElementName=languageComboBox, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding FilterSettings.LanguageVersion, UpdateSourceTrigger=PropertyChanged}"/>
</ToolBar>
<Border DockPanel.Dock="Top" BorderBrush="Black" BorderThickness="1" Name="updatePanel" Visibility="Collapsed">
<DockPanel KeyboardNavigation.TabNavigation="Contained">
<Button DockPanel.Dock="Right" Click="updatePanelCloseButtonClick" MinWidth="0">X</Button>
<StackPanel Orientation="Horizontal">
<TextBlock Name="updatePanelMessage" Margin="4,0" VerticalAlignment="Center" Text="{x:Static properties:Resources.ILSpyVersionAvailable}" />
<Button Name="downloadOrCheckUpdateButton" Click="downloadOrCheckUpdateButtonClick" Content="{x:Static properties:Resources.Download}"/>
</StackPanel>
</DockPanel>
</Border>
<!-- Status bar -->
<StatusBar x:Name="statusBar" DockPanel.Dock="Bottom" Height="26" Visibility="Collapsed">
<StatusBarItem DockPanel.Dock="Right">
@ -107,99 +117,40 @@ @@ -107,99 +117,40 @@
</StatusBarItem>
</StatusBar>
<!-- Main grid separating left pane (treeView) from main pane (textEditor) -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition
Name="leftColumn"
MinWidth="100"
Width="0.4*" />
<ColumnDefinition
Width="3" />
<ColumnDefinition
Name="rightColumn"
MinWidth="100"
Width="0.6*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition
Height="*" />
</Grid.RowDefinitions>
<!-- Left pane: Tree View of assemblies and classes -->
<tv:SharpTreeView
Name="treeView"
AutomationProperties.Name="Assemblies and Classes"
SelectionChanged="TreeView_SelectionChanged"
ShowRoot="False"
AllowDropOrder="True"
AllowDrop="True"
BorderThickness="0,1,1,1" Visibility="Visible" />
<GridSplitter
Grid.ZIndex="1"
Grid.Column="1"
Margin="-5,0"
BorderThickness="5,0"
HorizontalAlignment="Center"
VerticalAlignment="Stretch"
Focusable="False"
BorderBrush="Transparent" />
<!-- Right pane: Text Editor -->
<Grid Grid.Column="2">
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="0" Name="topPaneRow" />
<RowDefinition Height="0" />
<RowDefinition Height="0.7*" MinHeight="100" Name="textViewRow" />
<RowDefinition Height="0" />
<RowDefinition Height="0" Name="bottomPaneRow" />
</Grid.RowDefinitions>
<Border BorderBrush="Black" BorderThickness="1" Name="updatePanel" Visibility="Collapsed">
<DockPanel KeyboardNavigation.TabNavigation="Contained">
<Button DockPanel.Dock="Right" Click="updatePanelCloseButtonClick" MinWidth="0">X</Button>
<StackPanel Orientation="Horizontal">
<TextBlock Name="updatePanelMessage" Margin="4,0" VerticalAlignment="Center" Text="{x:Static properties:Resources.ILSpyVersionAvailable}" />
<Button Name="downloadOrCheckUpdateButton" Click="downloadOrCheckUpdateButtonClick" Content="{x:Static properties:Resources.Download}"/>
</StackPanel>
</DockPanel>
</Border>
<controls:DockedPane x:Name="topPane" Grid.Row="1" Title="Top" Visibility="Collapsed"
AutomationProperties.Name="Close top pane"
CloseButtonClicked="TopPane_CloseButtonClicked" Margin="0,0,0,3"
BorderThickness="1,1,0,1" />
<avalondock:DockingManager x:Name="DockManager"
AllowMixedOrientation="True" Grid.ColumnSpan="2">
<GridSplitter
Grid.ZIndex="1"
Grid.Row="2"
Margin="0,-2,0,-5"
BorderThickness="0,2,0,5"
BorderBrush="Transparent"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Focusable="False"
Visibility="{Binding Visibility, ElementName=topPane}" />
<avalondock:LayoutRoot x:Name="LayoutRoot" >
<avalondock:LayoutPanel Orientation="Vertical">
<avalondock:LayoutPanel Orientation="Horizontal">
<avalondock:LayoutAnchorablePaneGroup DockWidth="400" Orientation="Vertical">
<avalondock:LayoutAnchorablePane DockHeight="*">
<avalondock:LayoutAnchorable x:Name="Tree" Title="Assemblies" CanHide="False" CanClose="False">
<!-- Left pane: Tree View of assemblies and classes -->
<tv:SharpTreeView
Name="treeView"
AutomationProperties.Name="Assemblies and Classes"
SelectionChanged="TreeView_SelectionChanged"
ShowRoot="False"
AllowDropOrder="True"
AllowDrop="True"
BorderThickness="0,1,1,1" Visibility="Visible" />
</avalondock:LayoutAnchorable>
</avalondock:LayoutAnchorablePane>
</avalondock:LayoutAnchorablePaneGroup>
<!-- decompilerTextView is into the mainPane by code -->
<ContentPresenter Name="mainPane" Grid.Row="3"/>
<GridSplitter
Grid.ZIndex="1"
Grid.Row="4"
Margin="0,-2,0,-5"
BorderThickness="0,2,0,5"
BorderBrush="Transparent"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Focusable="False"
Visibility="{Binding Visibility, ElementName=bottomPane}" />
<controls:DockedPane x:Name="bottomPane" Grid.Row="5" Title="Bottom" Visibility="Collapsed"
AutomationProperties.Name="Close"
CloseButtonClicked="BottomPane_CloseButtonClicked" Margin="0,3,0,0" BorderThickness="1,1,0,1"/>
</Grid>
</Grid>
<avalondock:LayoutDocumentPaneGroup DockWidth="*">
<avalondock:LayoutDocumentPane x:Name="adDocumentPane">
<avalondock:LayoutDocument Title="View" CanClose="False">
<!-- decompilerTextView is into the mainPane by code -->
<ContentPresenter Name="mainPane" />
</avalondock:LayoutDocument>
</avalondock:LayoutDocumentPane>
</avalondock:LayoutDocumentPaneGroup>
</avalondock:LayoutPanel>
</avalondock:LayoutPanel>
</avalondock:LayoutRoot>
</avalondock:DockingManager>
</DockPanel>
</Window>

177
ILSpy/MainWindow.xaml.cs

@ -44,6 +44,8 @@ using ICSharpCode.ILSpy.TreeNodes; @@ -44,6 +44,8 @@ using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.TreeView;
using Microsoft.Win32;
using OSVersionHelper;
using Xceed.Wpf.AvalonDock.Layout;
using Xceed.Wpf.AvalonDock.Layout.Serialization;
namespace ICSharpCode.ILSpy
{
@ -90,10 +92,8 @@ namespace ICSharpCode.ILSpy @@ -90,10 +92,8 @@ namespace ICSharpCode.ILSpy
decompilerTextView = App.ExportProvider.GetExportedValue<DecompilerTextView>();
mainPane.Content = decompilerTextView;
if (sessionSettings.SplitterPosition > 0 && sessionSettings.SplitterPosition < 1) {
leftColumn.Width = new GridLength(sessionSettings.SplitterPosition, GridUnitType.Star);
rightColumn.Width = new GridLength(1 - sessionSettings.SplitterPosition, GridUnitType.Star);
}
sessionSettings.DockLayout.Deserialize(new XmlLayoutSerializer(DockManager));
sessionSettings.FilterSettings.PropertyChanged += filterSettings_PropertyChanged;
InitMainMenu();
@ -140,19 +140,15 @@ namespace ICSharpCode.ILSpy @@ -140,19 +140,15 @@ namespace ICSharpCode.ILSpy
Button MakeToolbarItem(Lazy<ICommand, IToolbarCommandMetadata> command)
{
object image = Images.Load(command.Value, command.Metadata.ToolbarIcon);
if (!(image is Viewbox)) {
image = new Image {
Width = 16,
Height = 16,
Source = (ImageSource)image
};
}
return new Button {
Command = CommandWrapper.Unwrap(command.Value),
ToolTip = Properties.Resources.ResourceManager.GetString(command.Metadata.ToolTip),
Tag = command.Metadata.Tag,
Content = image
Content = new Image {
Width = 16,
Height = 16,
Source = Images.Load(command.Value, command.Metadata.ToolbarIcon)
}
};
}
#endregion
@ -178,15 +174,11 @@ namespace ICSharpCode.ILSpy @@ -178,15 +174,11 @@ namespace ICSharpCode.ILSpy
if (!string.IsNullOrEmpty(GetResourceString(entry.Metadata.Header)))
menuItem.Header = GetResourceString(entry.Metadata.Header);
if (!string.IsNullOrEmpty(entry.Metadata.MenuIcon)) {
object image = Images.Load(entry.Value, entry.Metadata.MenuIcon);
if (!(image is Viewbox)) {
image = new Image {
Width = 16,
Height = 16,
Source = (ImageSource)image
};
}
menuItem.Icon = image;
menuItem.Icon = new Image {
Width = 16,
Height = 16,
Source = Images.Load(entry.Value, entry.Metadata.MenuIcon)
};
}
menuItem.IsEnabled = entry.Metadata.IsEnabled;
@ -331,6 +323,10 @@ namespace ICSharpCode.ILSpy @@ -331,6 +323,10 @@ namespace ICSharpCode.ILSpy
}
}
}
} else if (navigateTo == "none") {
// Don't navigate anywhere; start empty.
// Used by ILSpy VS addin, it'll send us the real location to navigate to via IPC.
found = true;
} else {
IEntity mr = await Task.Run(() => FindEntityInRelevantAssemblies(navigateTo, relevantAssemblies));
if (mr != null && mr.ParentModule.PEFile != null) {
@ -345,31 +341,31 @@ namespace ICSharpCode.ILSpy @@ -345,31 +341,31 @@ namespace ICSharpCode.ILSpy
output.Write(string.Format("Cannot find '{0}' in command line specified assemblies.", navigateTo));
decompilerTextView.ShowText(output);
}
} else if (commandLineLoadedAssemblies.Count == 1) {
} else if (relevantAssemblies.Count == 1) {
// NavigateTo == null and an assembly was given on the command-line:
// Select the newly loaded assembly
AssemblyTreeNode asmNode = assemblyListTreeNode.FindAssemblyNode(commandLineLoadedAssemblies[0]);
AssemblyTreeNode asmNode = assemblyListTreeNode.FindAssemblyNode(relevantAssemblies[0]);
if (asmNode != null && treeView.SelectedItem == initialSelection) {
SelectNode(asmNode);
}
} else if (spySettings != null) {
SharpTreeNode node = null;
if (sessionSettings.ActiveTreeViewPath?.Length > 0) {
if (activeTreeViewPath?.Length > 0) {
foreach (var asm in assemblyList.GetAssemblies()) {
if (asm.FileName == sessionSettings.ActiveTreeViewPath[0]) {
if (asm.FileName == activeTreeViewPath[0]) {
// FindNodeByPath() blocks the UI if the assembly is not yet loaded,
// so use an async wait instead.
await asm.GetPEFileAsync().Catch<Exception>(ex => { });
}
}
node = FindNodeByPath(sessionSettings.ActiveTreeViewPath, true);
node = FindNodeByPath(activeTreeViewPath, true);
}
if (treeView.SelectedItem == initialSelection) {
if (node != null) {
SelectNode(node);
// only if not showing the about page, perform the update check:
ShowMessageIfUpdatesAvailableAsync(spySettings);
await ShowMessageIfUpdatesAvailableAsync(spySettings);
} else {
AboutPage.Display(decompilerTextView);
}
@ -503,20 +499,21 @@ namespace ICSharpCode.ILSpy @@ -503,20 +499,21 @@ namespace ICSharpCode.ILSpy
#region Update Check
string updateAvailableDownloadUrl;
public void ShowMessageIfUpdatesAvailableAsync(ILSpySettings spySettings, bool forceCheck = false)
public async Task ShowMessageIfUpdatesAvailableAsync(ILSpySettings spySettings, bool forceCheck = false)
{
// Don't check for updates if we're in an MSIX since they work differently
if (WindowsVersionHelper.HasPackageIdentity) {
return;
}
Task<string> result;
string downloadUrl;
if (forceCheck) {
result = AboutPage.CheckForUpdatesAsync(spySettings);
downloadUrl = await AboutPage.CheckForUpdatesAsync(spySettings);
} else {
result = AboutPage.CheckForUpdatesIfEnabledAsync(spySettings);
downloadUrl = await AboutPage.CheckForUpdatesIfEnabledAsync(spySettings);
}
result.ContinueWith(task => AdjustUpdateUIAfterCheck(task, forceCheck), TaskScheduler.FromCurrentSynchronizationContext());
AdjustUpdateUIAfterCheck(downloadUrl, forceCheck);
}
void updatePanelCloseButtonClick(object sender, RoutedEventArgs e)
@ -524,22 +521,22 @@ namespace ICSharpCode.ILSpy @@ -524,22 +521,22 @@ namespace ICSharpCode.ILSpy
updatePanel.Visibility = Visibility.Collapsed;
}
void downloadOrCheckUpdateButtonClick(object sender, RoutedEventArgs e)
async void downloadOrCheckUpdateButtonClick(object sender, RoutedEventArgs e)
{
if (updateAvailableDownloadUrl != null) {
MainWindow.OpenLink(updateAvailableDownloadUrl);
} else {
updatePanel.Visibility = Visibility.Collapsed;
AboutPage.CheckForUpdatesAsync(ILSpySettings.Load())
.ContinueWith(task => AdjustUpdateUIAfterCheck(task, true), TaskScheduler.FromCurrentSynchronizationContext());
string downloadUrl = await AboutPage.CheckForUpdatesAsync(ILSpySettings.Load());
AdjustUpdateUIAfterCheck(downloadUrl, true);
}
}
void AdjustUpdateUIAfterCheck(Task<string> task, bool displayMessage)
void AdjustUpdateUIAfterCheck(string downloadUrl, bool displayMessage)
{
updateAvailableDownloadUrl = task.Result;
updateAvailableDownloadUrl = downloadUrl;
updatePanel.Visibility = displayMessage ? Visibility.Visible : Visibility.Collapsed;
if (task.Result != null) {
if (downloadUrl != null) {
updatePanelMessage.Text = Properties.Resources.ILSpyVersionAvailable;
downloadOrCheckUpdateButton.Content = Properties.Resources.Download;
} else {
@ -713,6 +710,8 @@ namespace ICSharpCode.ILSpy @@ -713,6 +710,8 @@ namespace ICSharpCode.ILSpy
return assemblyListTreeNode.FindAssemblyNode(asm);
case Resource res:
return assemblyListTreeNode.FindResourceNode(res);
case ValueTuple<Resource, string> resName:
return assemblyListTreeNode.FindResourceNode(resName.Item1, resName.Item2);
case ITypeDefinition type:
return assemblyListTreeNode.FindTypeNode(type);
case IField fd:
@ -1016,11 +1015,7 @@ namespace ICSharpCode.ILSpy @@ -1016,11 +1015,7 @@ namespace ICSharpCode.ILSpy
sessionSettings.ActiveTreeViewPath = GetPathForNode(treeView.SelectedItem as SharpTreeNode);
sessionSettings.ActiveAutoLoadedAssembly = GetAutoLoadedAssemblyNode(treeView.SelectedItem as SharpTreeNode);
sessionSettings.WindowBounds = this.RestoreBounds;
sessionSettings.SplitterPosition = leftColumn.Width.Value / (leftColumn.Width.Value + rightColumn.Width.Value);
if (topPane.Visibility == Visibility.Visible)
sessionSettings.TopPaneSplitterPosition = topPaneRow.Height.Value / (topPaneRow.Height.Value + textViewRow.Height.Value);
if (bottomPane.Visibility == Visibility.Visible)
sessionSettings.BottomPaneSplitterPosition = bottomPaneRow.Height.Value / (bottomPaneRow.Height.Value + textViewRow.Height.Value);
sessionSettings.DockLayout.Serialize(new XmlLayoutSerializer(DockManager));
sessionSettings.Save();
}
@ -1042,95 +1037,19 @@ namespace ICSharpCode.ILSpy @@ -1042,95 +1037,19 @@ namespace ICSharpCode.ILSpy
#region Top/Bottom Pane management
/// <summary>
/// When grid is resized using splitter, row height value could become greater than 1.
/// As result, when a new pane is shown, both textView and pane could become very small.
/// This method normalizes two rows and ensures that height is less then 1.
/// </summary>
void NormalizePaneRowHeightValues(RowDefinition pane1Row, RowDefinition pane2Row)
{
var pane1Height = pane1Row.Height;
var pane2Height = pane2Row.Height;
//only star height values are normalized.
if (!pane1Height.IsStar || !pane2Height.IsStar) {
return;
}
var totalHeight = pane1Height.Value + pane2Height.Value;
if (totalHeight == 0) {
return;
}
pane1Row.Height = new GridLength(pane1Height.Value / totalHeight, GridUnitType.Star);
pane2Row.Height = new GridLength(pane2Height.Value / totalHeight, GridUnitType.Star);
}
public void ShowInTopPane(string title, object content)
{
topPaneRow.MinHeight = 100;
if (sessionSettings.TopPaneSplitterPosition > 0 && sessionSettings.TopPaneSplitterPosition < 1) {
//Ensure all 3 blocks are in fair conditions
NormalizePaneRowHeightValues(bottomPaneRow, textViewRow);
textViewRow.Height = new GridLength(1 - sessionSettings.TopPaneSplitterPosition, GridUnitType.Star);
topPaneRow.Height = new GridLength(sessionSettings.TopPaneSplitterPosition, GridUnitType.Star);
}
topPane.Title = title;
if (topPane.Content != content) {
IPane pane = topPane.Content as IPane;
if (pane != null)
pane.Closed();
topPane.Content = content;
}
topPane.Visibility = Visibility.Visible;
}
void TopPane_CloseButtonClicked(object sender, EventArgs e)
public void ShowInNewPane(string title, object content, PanePosition panePosition, string toolTip = null)
{
sessionSettings.TopPaneSplitterPosition = topPaneRow.Height.Value / (topPaneRow.Height.Value + textViewRow.Height.Value);
topPaneRow.MinHeight = 0;
topPaneRow.Height = new GridLength(0);
topPane.Visibility = Visibility.Collapsed;
IPane pane = topPane.Content as IPane;
topPane.Content = null;
if (pane != null)
pane.Closed();
}
public void ShowInBottomPane(string title, object content)
{
bottomPaneRow.MinHeight = 100;
if (sessionSettings.BottomPaneSplitterPosition > 0 && sessionSettings.BottomPaneSplitterPosition < 1) {
//Ensure all 3 blocks are in fair conditions
NormalizePaneRowHeightValues(topPaneRow, textViewRow);
textViewRow.Height = new GridLength(1 - sessionSettings.BottomPaneSplitterPosition, GridUnitType.Star);
bottomPaneRow.Height = new GridLength(sessionSettings.BottomPaneSplitterPosition, GridUnitType.Star);
}
bottomPane.Title = title;
if (bottomPane.Content != content) {
IPane pane = bottomPane.Content as IPane;
if (pane != null)
pane.Closed();
bottomPane.Content = content;
if (panePosition == PanePosition.Document) {
var layoutDocument = new LayoutDocument() { Title = title, Content = content, ToolTip = toolTip, CanClose = true };
var documentPane = this.DockManager.Layout.Descendents().OfType<LayoutDocumentPane>().FirstOrDefault();
documentPane.Children.Add(layoutDocument);
} else {
var layoutAnchorable = new LayoutAnchorable() { Title = title, Content = content, ToolTip = toolTip, CanClose = true, CanHide = true };
var documentPane = this.DockManager.Layout.Descendents().OfType<LayoutDocumentPane>().FirstOrDefault();
Docking.DockingHelper.DockHorizontal(layoutAnchorable, documentPane, new GridLength(200), panePosition == PanePosition.Top);
}
bottomPane.Visibility = Visibility.Visible;
}
void BottomPane_CloseButtonClicked(object sender, EventArgs e)
{
sessionSettings.BottomPaneSplitterPosition = bottomPaneRow.Height.Value / (bottomPaneRow.Height.Value + textViewRow.Height.Value);
bottomPaneRow.MinHeight = 0;
bottomPaneRow.Height = new GridLength(0);
bottomPane.Visibility = Visibility.Collapsed;
IPane pane = bottomPane.Content as IPane;
bottomPane.Content = null;
if (pane != null)
pane.Closed();
}
#endregion
public void UnselectAll()

9
ILSpy/Properties/Resources.Designer.cs generated

@ -1008,6 +1008,15 @@ namespace ICSharpCode.ILSpy.Properties { @@ -1008,6 +1008,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Decompile to new tab.
/// </summary>
public static string DecompileToNewPanel {
get {
return ResourceManager.GetString("DecompileToNewPanel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Decompiling....
/// </summary>

3
ILSpy/Properties/Resources.resx

@ -766,4 +766,7 @@ Are you sure you want to continue?</value> @@ -766,4 +766,7 @@ Are you sure you want to continue?</value>
<data name="Assembly" xml:space="preserve">
<value>Assembly</value>
</data>
<data name="DecompileToNewPanel" xml:space="preserve">
<value>Decompile to new tab</value>
</data>
</root>

122
ILSpy/Search/AbstractEntitySearchStrategy.cs

@ -0,0 +1,122 @@ @@ -0,0 +1,122 @@
using System;
using System.Collections.Concurrent;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Media;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TreeNodes;
namespace ICSharpCode.ILSpy.Search
{
abstract class AbstractEntitySearchStrategy : AbstractSearchStrategy
{
protected readonly Language language;
protected readonly ApiVisibility apiVisibility;
protected AbstractEntitySearchStrategy(Language language, ApiVisibility apiVisibility, IProducerConsumerCollection<SearchResult> resultQueue, params string[] terms)
: base(resultQueue, terms)
{
this.language = language;
this.apiVisibility = apiVisibility;
}
protected bool CheckVisibility(IEntity entity)
{
if (apiVisibility == ApiVisibility.All)
return true;
do {
if (apiVisibility == ApiVisibility.PublicOnly) {
if (!(entity.Accessibility == Accessibility.Public ||
entity.Accessibility == Accessibility.Protected ||
entity.Accessibility == Accessibility.ProtectedOrInternal))
return false;
} else if (apiVisibility == ApiVisibility.PublicAndInternal) {
if (!language.ShowMember(entity))
return false;
}
entity = entity.DeclaringTypeDefinition;
}
while (entity != null);
return true;
}
protected void OnFoundResult(IEntity entity)
{
var result = ResultFromEntity(entity);
OnFoundResult(result);
}
SearchResult ResultFromEntity(IEntity item)
{
var declaringType = item.DeclaringTypeDefinition;
return new MemberSearchResult {
Member = item,
Fitness = CalculateFitness(item),
Name = GetLanguageSpecificName(item),
Location = declaringType != null ? language.TypeToString(declaringType, includeNamespace: true) : item.Namespace,
Assembly = item.ParentModule.FullAssemblyName,
ToolTip = item.ParentModule.PEFile?.FileName
};
}
float CalculateFitness(IEntity member)
{
string text = member.Name;
// Probably compiler generated types without meaningful names, show them last
if (text.StartsWith("<")) {
return 0;
}
// Constructors always have the same name in IL:
// Use type name instead
if (text == ".cctor" || text == ".ctor") {
text = member.DeclaringType.Name;
}
// Ignore generic arguments, it not possible to search based on them either
text = ReflectionHelper.SplitTypeParameterCountFromReflectionName(text);
return 1.0f / text.Length;
}
string GetLanguageSpecificName(IEntity member)
{
switch (member) {
case ITypeDefinition t:
return language.TypeToString(t, false);
case IField f:
return language.FieldToString(f, true, false, false);
case IProperty p:
return language.PropertyToString(p, true, false, false);
case IMethod m:
return language.MethodToString(m, true, false, false);
case IEvent e:
return language.EventToString(e, true, false, false);
default:
throw new NotSupportedException(member?.GetType() + " not supported!");
}
}
static internal ImageSource GetIcon(IEntity member)
{
switch (member) {
case ITypeDefinition t:
return TypeTreeNode.GetIcon(t);
case IField f:
return FieldTreeNode.GetIcon(f);
case IProperty p:
return PropertyTreeNode.GetIcon(p);
case IMethod m:
return MethodTreeNode.GetIcon(m);
case IEvent e:
return EventTreeNode.GetIcon(e);
default:
throw new NotSupportedException(member?.GetType() + " not supported!");
}
}
}
}

107
ILSpy/Search/AbstractSearchStrategy.cs

@ -15,14 +15,10 @@ namespace ICSharpCode.ILSpy.Search @@ -15,14 +15,10 @@ namespace ICSharpCode.ILSpy.Search
protected readonly Regex regex;
protected readonly bool fullNameSearch;
protected readonly bool omitGenerics;
protected readonly Language language;
protected readonly ApiVisibility apiVisibility;
private readonly IProducerConsumerCollection<SearchResult> resultQueue;
protected AbstractSearchStrategy(Language language, ApiVisibility apiVisibility, IProducerConsumerCollection<SearchResult> resultQueue, params string[] terms)
protected AbstractSearchStrategy(IProducerConsumerCollection<SearchResult> resultQueue, params string[] terms)
{
this.language = language;
this.apiVisibility = apiVisibility;
this.resultQueue = resultQueue;
if (terms.Length == 1 && terms[0].Length > 2) {
@ -44,17 +40,17 @@ namespace ICSharpCode.ILSpy.Search @@ -44,17 +40,17 @@ namespace ICSharpCode.ILSpy.Search
public abstract void Search(PEFile module, CancellationToken cancellationToken);
protected virtual bool IsMatch(string entityName)
protected virtual bool IsMatch(string name)
{
if (regex != null) {
return regex.IsMatch(entityName);
return regex.IsMatch(name);
}
for (int i = 0; i < searchTerm.Length; ++i) {
// How to handle overlapping matches?
var term = searchTerm[i];
if (string.IsNullOrEmpty(term)) continue;
string text = entityName;
string text = name;
switch (term[0]) {
case '+': // must contain
term = term.Substring(1);
@ -87,28 +83,6 @@ namespace ICSharpCode.ILSpy.Search @@ -87,28 +83,6 @@ namespace ICSharpCode.ILSpy.Search
return true;
}
protected bool CheckVisibility(IEntity entity)
{
if (apiVisibility == ApiVisibility.All)
return true;
do {
if (apiVisibility == ApiVisibility.PublicOnly) {
if (!(entity.Accessibility == Accessibility.Public ||
entity.Accessibility == Accessibility.Protected ||
entity.Accessibility == Accessibility.ProtectedOrInternal))
return false;
} else if (apiVisibility == ApiVisibility.PublicAndInternal) {
if (!language.ShowMember(entity))
return false;
}
entity = entity.DeclaringTypeDefinition;
}
while (entity != null);
return true;
}
bool IsNoncontiguousMatch(string text, string searchTerm)
{
if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(searchTerm)) {
@ -136,9 +110,8 @@ namespace ICSharpCode.ILSpy.Search @@ -136,9 +110,8 @@ namespace ICSharpCode.ILSpy.Search
return false;
}
protected void OnFoundResult(IEntity entity)
protected void OnFoundResult(SearchResult result)
{
var result = ResultFromEntity(entity);
resultQueue.TryAdd(result);
}
@ -150,75 +123,5 @@ namespace ICSharpCode.ILSpy.Search @@ -150,75 +123,5 @@ namespace ICSharpCode.ILSpy.Search
return null;
}
}
SearchResult ResultFromEntity(IEntity item)
{
var declaringType = item.DeclaringTypeDefinition;
return new SearchResult {
Member = item,
Fitness = CalculateFitness(item),
Name = GetLanguageSpecificName(item),
Location = declaringType != null ? language.TypeToString(declaringType, includeNamespace: true) : item.Namespace,
Assembly = item.ParentModule.FullAssemblyName,
ToolTip = item.ParentModule.PEFile?.FileName
};
}
float CalculateFitness(IEntity member)
{
string text = member.Name;
// Probably compiler generated types without meaningful names, show them last
if (text.StartsWith("<")) {
return 0;
}
// Constructors always have the same name in IL:
// Use type name instead
if (text == ".cctor" || text == ".ctor") {
text = member.DeclaringType.Name;
}
// Ignore generic arguments, it not possible to search based on them either
text = ReflectionHelper.SplitTypeParameterCountFromReflectionName(text);
return 1.0f / text.Length;
}
string GetLanguageSpecificName(IEntity member)
{
switch (member) {
case ITypeDefinition t:
return language.TypeToString(t, false);
case IField f:
return language.FieldToString(f, true, false, false);
case IProperty p:
return language.PropertyToString(p, true, false, false);
case IMethod m:
return language.MethodToString(m, true, false, false);
case IEvent e:
return language.EventToString(e, true, false, false);
default:
throw new NotSupportedException(member?.GetType() + " not supported!");
}
}
internal static ImageSource GetIcon(IEntity member)
{
switch (member) {
case ITypeDefinition t:
return TypeTreeNode.GetIcon(t);
case IField f:
return FieldTreeNode.GetIcon(f);
case IProperty p:
return PropertyTreeNode.GetIcon(p);
case IMethod m:
return MethodTreeNode.GetIcon(m);
case IEvent e:
return EventTreeNode.GetIcon(e);
default:
throw new NotSupportedException(member?.GetType() + " not supported!");
}
}
}
}

111
ILSpy/Search/AssemblySearchStrategy.cs

@ -0,0 +1,111 @@ @@ -0,0 +1,111 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Reflection.Metadata;
using System.Threading;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.TreeView;
namespace ICSharpCode.ILSpy.Search
{
class AssemblySearchStrategy : AbstractSearchStrategy
{
readonly AssemblySearchKind searchKind;
public AssemblySearchStrategy(string term, IProducerConsumerCollection<SearchResult> resultQueue, AssemblySearchKind searchKind)
: this(resultQueue, new[] { term }, searchKind)
{
}
public AssemblySearchStrategy(IProducerConsumerCollection<SearchResult> resultQueue, string[] terms, AssemblySearchKind searchKind)
: base(resultQueue, terms)
{
this.searchKind = searchKind;
}
public override void Search(PEFile module, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (searchKind == AssemblySearchKind.NameOrFileName) {
string localName = GetNameToMatch(module, AssemblySearchKind.Name);
string fileName = Path.GetFileName(GetNameToMatch(module, AssemblySearchKind.FilePath));
if (IsMatch(localName) || IsMatch(fileName))
OnFoundResult(module);
return;
}
string name = GetNameToMatch(module, searchKind);
if (IsMatch(name))
OnFoundResult(module);
}
string GetNameToMatch(PEFile module, AssemblySearchKind kind)
{
switch (kind) {
case AssemblySearchKind.FullName:
return module.FullName;
case AssemblySearchKind.Name:
return module.Name;
case AssemblySearchKind.FilePath:
return module.FileName;
}
if (!module.IsAssembly)
return null;
var metadata = module.Metadata;
var definition = module.Metadata.GetAssemblyDefinition();
switch (kind) {
case AssemblySearchKind.Culture:
if (definition.Culture.IsNil)
return "neutral";
return metadata.GetString(definition.Culture);
case AssemblySearchKind.Version:
return definition.Version.ToString();
case AssemblySearchKind.PublicKey:
return module.Metadata.GetPublicKeyToken();
case AssemblySearchKind.HashAlgorithm:
return definition.HashAlgorithm.ToString();
case AssemblySearchKind.Flags:
return definition.Flags.ToString();
}
return null;
}
void OnFoundResult(PEFile module)
{
var result = new AssemblySearchResult {
Module = module,
Fitness = 1.0f / module.Name.Length,
Name = module.Name,
Location = module.FileName,
Assembly = module.FullName,
ToolTip = module.FileName,
};
OnFoundResult(result);
}
}
enum AssemblySearchKind
{
NameOrFileName,
Name,
FullName,
FilePath,
Culture,
Version,
PublicKey,
HashAlgorithm,
Flags
}
}

2
ILSpy/Search/LiteralSearchStrategy.cs

@ -15,7 +15,7 @@ using System.Collections.Concurrent; @@ -15,7 +15,7 @@ using System.Collections.Concurrent;
namespace ICSharpCode.ILSpy.Search
{
class LiteralSearchStrategy : AbstractSearchStrategy
class LiteralSearchStrategy : AbstractEntitySearchStrategy
{
readonly TypeCode searchTermLiteralType;
readonly object searchTermLiteralValue;

2
ILSpy/Search/MemberSearchStrategy.cs

@ -6,7 +6,7 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -6,7 +6,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.ILSpy.Search
{
class MemberSearchStrategy : AbstractSearchStrategy
class MemberSearchStrategy : AbstractEntitySearchStrategy
{
readonly MemberSearchKind searchKind;

2
ILSpy/Search/MetadataTokenSearchStrategy.cs

@ -9,7 +9,7 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -9,7 +9,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.ILSpy.Search
{
class MetadataTokenSearchStrategy : AbstractSearchStrategy
class MetadataTokenSearchStrategy : AbstractEntitySearchStrategy
{
readonly EntityHandle searchTermToken;

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save