Browse Source

Merge branch 'master' of https://github.com/icsharpcode/ILSpy

pull/2066/head
MysticBoy 5 years ago
parent
commit
d96b546db8
  1. 2
      DecompilerNuGetDemos.workbook
  2. 8
      ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj
  3. 2
      ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj
  4. 5
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  5. 2
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  6. 18
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
  7. 13
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs
  8. 48
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  9. 12
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  10. 6
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  11. 3
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs
  12. 20
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  13. 3
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
  14. 22
      ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs
  15. 29
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  16. 67
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs
  17. 5
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs
  18. 1
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs
  19. 63
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  20. 20
      ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs
  21. 20
      ICSharpCode.Decompiler/DecompilerSettings.cs
  22. 6
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  23. 27
      ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs
  24. 2
      ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs
  25. 2
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  26. 8
      ICSharpCode.Decompiler/IL/ILReader.cs
  27. 14
      ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
  28. 1
      ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs
  29. 11
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  30. 9
      ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs
  31. 10
      ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs
  32. 24
      ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
  33. 2
      ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs
  34. 4
      ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
  35. 2
      ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs
  36. 2
      ICSharpCode.Decompiler/IL/Transforms/FixLoneIsInst.cs
  37. 36
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
  38. 19
      ICSharpCode.Decompiler/IL/Transforms/IndexRangeTransform.cs
  39. 2
      ICSharpCode.Decompiler/IL/Transforms/IntroduceDynamicTypeOnLocals.cs
  40. 2
      ICSharpCode.Decompiler/IL/Transforms/IntroduceRefReadOnlyModifierOnLocals.cs
  41. 24
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
  42. 2
      ICSharpCode.Decompiler/IL/Transforms/LockTransform.cs
  43. 4
      ICSharpCode.Decompiler/IL/Transforms/NamedArgumentTransform.cs
  44. 2
      ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs
  45. 2
      ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs
  46. 2
      ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs
  47. 2
      ICSharpCode.Decompiler/IL/Transforms/SwitchOnNullableTransform.cs
  48. 2
      ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs
  49. 2
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
  50. 2
      ICSharpCode.Decompiler/IL/Transforms/UserDefinedLogicTransform.cs
  51. 2
      ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs
  52. 55
      ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs
  53. 33
      ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs
  54. 4
      ICSharpCode.Decompiler/Metadata/PEFile.cs
  55. 30
      ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs
  56. 63
      ICSharpCode.Decompiler/Output/TextTokenWriter.cs
  57. 6
      ICSharpCode.Decompiler/TypeSystem/IMethod.cs
  58. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs
  59. 41
      ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs
  60. 22
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs
  61. 3
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs
  62. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs
  63. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/SyntheticRangeIndexer.cs
  64. 1
      ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs
  65. 8
      ICSharpCode.Decompiler/Util/CollectionExtensions.cs
  66. 23
      ILSpy.AddIn/AssemblyFileFinder.cs
  67. 14
      ILSpy.AddIn/Commands/OpenCodeItemCommand.cs
  68. 5
      ILSpy.AddIn/Commands/OpenILSpyCommand.cs
  69. 39
      ILSpy.AddIn/ILSpy.AddIn.csproj
  70. 6
      ILSpy.AddIn/ILSpyInstance.cs
  71. 8
      ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj
  72. 15
      ILSpy/App.xaml.cs
  73. 44
      ILSpy/Commands/DecompileAllCommand.cs
  74. 9
      ILSpy/Commands/DisassembleAllCommand.cs
  75. 4
      ILSpy/ILSpy.csproj
  76. 2
      ILSpy/Languages/CSharpHighlightingTokenWriter.cs
  77. 3
      ILSpy/Languages/CSharpLanguage.cs
  78. 42
      ILSpy/LoadedAssembly.cs
  79. 9
      ILSpy/MainWindow.xaml.cs
  80. 1
      ILSpy/Metadata/Helpers.cs
  81. 2
      ILSpy/Properties/AssemblyInfo.template.cs
  82. 7
      ILSpy/SolutionWriter.cs

2
DecompilerNuGetDemos.workbook

@ -6,7 +6,7 @@ platforms: @@ -6,7 +6,7 @@ platforms:
- DotNetCore
packages:
- id: ICSharpCode.Decompiler
version: 6.0.0.5836
version: 6.1.0.5902
---
Setup: load the references required to work with the decompiler

8
ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj

@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
<PackAsTool>true</PackAsTool>
<AssemblyName>ilspycmd</AssemblyName>
<ToolCommandName>ilspycmd</ToolCommandName>
<Version>6.0.0.5836</Version>
<Version>6.1.0.5902</Version>
<Description>Command-line decompiler using the ILSpy decompilation engine</Description>
<Copyright>Copyright 2011-2020 AlphaSierraPapa</Copyright>
<PackageProjectUrl>https://github.com/icsharpcode/ILSpy/</PackageProjectUrl>
@ -16,8 +16,8 @@ @@ -16,8 +16,8 @@
<PackageIcon>ILSpyCmdNuGetPackageIcon.png</PackageIcon>
<RepositoryUrl>https://github.com/icsharpcode/ILSpy/</RepositoryUrl>
<Company />
<AssemblyVersion>6.0.0.0</AssemblyVersion>
<FileVersion>6.0.0.0</FileVersion>
<AssemblyVersion>6.1.0.0</AssemblyVersion>
<FileVersion>6.1.0.0</FileVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>ILSpy Team</Authors>
</PropertyGroup>
@ -42,7 +42,7 @@ @@ -42,7 +42,7 @@
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<PackageReference Include="ICSharpCode.Decompiler" Version="6.0.0.5836" />
<PackageReference Include="ICSharpCode.Decompiler" Version="6.1.0.5902" />
</ItemGroup>
<ItemGroup>

2
ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj

@ -17,7 +17,7 @@ @@ -17,7 +17,7 @@
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<PackageReference Include="ICSharpCode.Decompiler" Version="6.0.0.5836" />
<PackageReference Include="ICSharpCode.Decompiler" Version="6.1.0.5902" />
</ItemGroup>
<ItemGroup>

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

@ -185,7 +185,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -185,7 +185,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
return Regex.Replace(il, @"'<PrivateImplementationDetails>\{[0-9A-F-]+\}'", "'<PrivateImplementationDetails>'");
}
static readonly string coreRefAsmPath = new DotNetCorePathFinder(new Version(3, 1)).GetReferenceAssemblyPath(".NETCoreApp, Version = v3.1");
static readonly string coreRefAsmPath = new DotNetCorePathFinder(TargetFrameworkIdentifier.NETCoreApp, new Version(3, 1)).GetReferenceAssemblyPath(".NETCoreApp, Version = v3.1");
static readonly string refAsmPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
@"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2");
@ -257,6 +257,9 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -257,6 +257,9 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
preprocessorSymbols.Add("LEGACY_CSC");
preprocessorSymbols.Add("LEGACY_VBC");
}
if (flags.HasFlag(CompilerOptions.Preview)) {
preprocessorSymbols.Add("CS90");
}
return preprocessorSymbols;
}

2
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -199,7 +199,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -199,7 +199,7 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void LocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview);
}
[Test]

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

@ -23,6 +23,12 @@ namespace LocalFunctions @@ -23,6 +23,12 @@ namespace LocalFunctions
{
internal class LocalFunctions
{
[AttributeUsage(AttributeTargets.All)]
internal class MyAttribute : Attribute
{
}
public class Generic<T1> where T1 : struct, ICloneable, IConvertible
{
public int MixedLocalFunction<T2>() where T2 : ICloneable, IConvertible
@ -31,15 +37,21 @@ namespace LocalFunctions @@ -31,15 +37,21 @@ namespace LocalFunctions
object z = this;
for (int j = 0; j < 10; j++) {
int i = 0;
i += NonStaticMethod6<object>();
int NonStaticMethod6<T3>()
i += NonStaticMethod6<object>(0);
#if CS90
[My]
[return: My]
int NonStaticMethod6<[My] T3>([My] int unused)
#else
int NonStaticMethod6<T3>(int unused)
#endif
{
t2 = default(T2);
int l = 0;
return NonStaticMethod6_1<T1>() + NonStaticMethod6_1<T2>() + z.GetHashCode();
int NonStaticMethod6_1<T4>()
{
return i + l + NonStaticMethod6<T4>() + StaticMethod1<decimal>();
return i + l + NonStaticMethod6<T4>(0) + StaticMethod1<decimal>();
}
}
}

13
ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs

@ -356,5 +356,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -356,5 +356,18 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
Console.WriteLine("normal exit");
}
internal IEnumerable<int> ForLoopWithYieldReturn(int end, int evil)
{
// This loop needs to pick the implicit "yield break;" as exit point
// in order to produce pretty code; not the "throw" which would
// be a less-pretty option.
for (int i = 0; i < end; i++) {
if (i == evil) {
throw new InvalidOperationException("Found evil number");
}
yield return i;
}
}
}
}

48
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -120,18 +120,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -120,18 +120,12 @@ namespace ICSharpCode.Decompiler.CSharp
new DetectExitPoints(canIntroduceExitForReturn: true),
new BlockILTransform { // per-block transforms
PostOrderTransforms = {
//new UseExitPoints(),
new ConditionDetection(),
new LockTransform(),
new UsingTransform(),
// CachedDelegateInitialization must run after ConditionDetection and before/in LoopingBlockTransform
// and must run before NullCoalescingTransform
new CachedDelegateInitialization(),
// Run the assignment transform both before and after copy propagation.
// Before is necessary because inline assignments of constants are otherwise
// copy-propated (turned into two separate assignments of the constant).
// After is necessary because the assigned value might involve null coalescing/etc.
new StatementTransform(new ILInlining(), new TransformAssignment()),
new StatementTransform(
// per-block transforms that depend on each other, and thus need to
// run interleaved (statement by statement).
@ -411,6 +405,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -411,6 +405,7 @@ namespace ICSharpCode.Decompiler.CSharp
typeSystemAstBuilder.AlwaysUseShortTypeNames = true;
typeSystemAstBuilder.AddResolveResultAnnotations = true;
typeSystemAstBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables;
typeSystemAstBuilder.SupportInitAccessors = settings.InitAccessors;
return typeSystemAstBuilder;
}
@ -1351,30 +1346,35 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1351,30 +1346,35 @@ namespace ICSharpCode.Decompiler.CSharp
}
entityDecl.AddAnnotation(function);
if (function.IsIterator) {
if (localSettings.DecompileMemberBodies && !body.Descendants.Any(d => d is YieldReturnStatement || d is YieldBreakStatement)) {
body.Add(new YieldBreakStatement());
}
if (function.IsAsync) {
RemoveAttribute(entityDecl, KnownAttribute.AsyncIteratorStateMachine);
} else {
RemoveAttribute(entityDecl, KnownAttribute.IteratorStateMachine);
}
if (function.StateMachineCompiledWithMono) {
RemoveAttribute(entityDecl, KnownAttribute.DebuggerHidden);
}
CleanUpMethodDeclaration(entityDecl, body, function, localSettings.DecompileMemberBodies);
} catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) {
throw new DecompilerException(module, method, innerException);
}
}
internal static void CleanUpMethodDeclaration(EntityDeclaration entityDecl, BlockStatement body, ILFunction function, bool decompileBody = true)
{
if (function.IsIterator) {
if (decompileBody && !body.Descendants.Any(d => d is YieldReturnStatement || d is YieldBreakStatement)) {
body.Add(new YieldBreakStatement());
}
if (function.IsAsync) {
entityDecl.Modifiers |= Modifiers.Async;
RemoveAttribute(entityDecl, KnownAttribute.AsyncStateMachine);
RemoveAttribute(entityDecl, KnownAttribute.DebuggerStepThrough);
RemoveAttribute(entityDecl, KnownAttribute.AsyncIteratorStateMachine);
} else {
RemoveAttribute(entityDecl, KnownAttribute.IteratorStateMachine);
}
} catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) {
throw new DecompilerException(module, method, innerException);
if (function.StateMachineCompiledWithMono) {
RemoveAttribute(entityDecl, KnownAttribute.DebuggerHidden);
}
}
if (function.IsAsync) {
entityDecl.Modifiers |= Modifiers.Async;
RemoveAttribute(entityDecl, KnownAttribute.AsyncStateMachine);
RemoveAttribute(entityDecl, KnownAttribute.DebuggerStepThrough);
}
}
bool RemoveAttribute(EntityDeclaration entityDecl, KnownAttribute attributeType)
internal static bool RemoveAttribute(EntityDeclaration entityDecl, KnownAttribute attributeType)
{
bool found = false;
foreach (var section in entityDecl.Attributes) {

12
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -197,8 +197,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -197,8 +197,7 @@ namespace ICSharpCode.Decompiler.CSharp
} else if (localFunction != null) {
var ide = new IdentifierExpression(localFunction.Name);
if (method.TypeArguments.Count > 0) {
int skipCount = localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters;
ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType));
ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
}
ide.AddAnnotation(localFunction);
target = ide.WithoutILInstruction()
@ -1327,7 +1326,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1327,7 +1326,6 @@ namespace ICSharpCode.Decompiler.CSharp
target = default;
targetType = default;
methodName = localFunction.Name;
// TODO : think about how to handle generic local functions
} else if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) {
step = 5;
targetType = method.Parameters[0].Type;
@ -1406,11 +1404,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1406,11 +1404,7 @@ namespace ICSharpCode.Decompiler.CSharp
} else {
var ide = new IdentifierExpression(methodName);
if ((step & 2) != 0) {
int skipCount = 0;
if (localFunction != null && method.TypeArguments.Count > 0) {
skipCount = localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters;
}
ide.TypeArguments.AddRange(method.TypeArguments.Skip(skipCount).Select(expressionBuilder.ConvertType));
ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
}
targetExpression = ide.WithRR(result);
}
@ -1469,7 +1463,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1469,7 +1463,7 @@ namespace ICSharpCode.Decompiler.CSharp
method.DeclaringType,
new IParameterizedMember[] { method }
)
}, method.TypeArguments.Skip(localFunction.ReducedMethod.NumberOfCompilerGeneratedTypeParameters).ToArray()
}, method.TypeArguments
);
}

6
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -92,6 +92,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -92,6 +92,7 @@ namespace ICSharpCode.Decompiler.CSharp
this.astBuilder = new TypeSystemAstBuilder(resolver);
this.astBuilder.AlwaysUseShortTypeNames = true;
this.astBuilder.AddResolveResultAnnotations = true;
this.astBuilder.ShowAttributes = true;
this.astBuilder.UseNullableSpecifierForValueTypes = settings.LiftNullables;
this.typeInference = new TypeInference(compilation) { Algorithm = TypeInferenceAlgorithm.Improved };
}
@ -1983,7 +1984,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1983,7 +1984,7 @@ namespace ICSharpCode.Decompiler.CSharp
return SpecialType.UnknownType;
}
internal IEnumerable<ParameterDeclaration> MakeParameters(IReadOnlyList<IParameter> parameters, ILFunction function)
IEnumerable<ParameterDeclaration> MakeParameters(IReadOnlyList<IParameter> parameters, ILFunction function)
{
var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index);
int i = 0;
@ -1992,9 +1993,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1992,9 +1993,6 @@ namespace ICSharpCode.Decompiler.CSharp
if (string.IsNullOrEmpty(pd.Name) && !pd.Type.IsArgList()) {
// needs to be consistent with logic in ILReader.CreateILVarable(ParameterDefinition)
pd.Name = "P_" + i;
// if this is a local function, we have to skip the parameters for closure references
if (settings.LocalFunctions && function.Kind == ILFunctionKind.LocalFunction && IL.Transforms.LocalFunctionDecompiler.IsClosureParameter(parameter, decompilationContext))
break;
}
if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType())
pd.Type = null;

3
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs

@ -24,6 +24,7 @@ using System.Linq; @@ -24,6 +24,7 @@ using System.Linq;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{
@ -222,7 +223,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -222,7 +223,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
{
TypeSystemAstBuilder astBuilder = CreateAstBuilder();
EntityDeclaration node = astBuilder.ConvertEntity(member);
if ((ConversionFlags & ConversionFlags.ShowDeclaringType) == ConversionFlags.ShowDeclaringType && member.DeclaringType != null) {
if ((ConversionFlags & ConversionFlags.ShowDeclaringType) == ConversionFlags.ShowDeclaringType && member.DeclaringType != null && !(member is LocalFunctionMethod)) {
ConvertType(member.DeclaringType, writer, formattingPolicy);
writer.WriteToken(Roles.Dot, ".");
}

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

@ -1876,19 +1876,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1876,19 +1876,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement)
{
StartNode(localFunctionDeclarationStatement);
WriteModifiers(localFunctionDeclarationStatement.ModifierTokens);
localFunctionDeclarationStatement.ReturnType.AcceptVisitor(this);
Space();
WriteIdentifier(localFunctionDeclarationStatement.NameToken);
WriteTypeParameters(localFunctionDeclarationStatement.TypeParameters);
Space(policy.SpaceBeforeMethodDeclarationParentheses);
WriteCommaSeparatedListInParenthesis(localFunctionDeclarationStatement.Parameters, policy.SpaceWithinMethodDeclarationParentheses);
foreach (Constraint constraint in localFunctionDeclarationStatement.Constraints) {
constraint.AcceptVisitor(this);
}
WriteMethodBody(localFunctionDeclarationStatement.Body, policy.MethodBraceStyle);
localFunctionDeclarationStatement.Declaration.AcceptVisitor(this);
EndNode(localFunctionDeclarationStatement);
}
@ -1939,7 +1927,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1939,7 +1927,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
WriteKeyword("get", PropertyDeclaration.GetKeywordRole);
style = policy.PropertyGetBraceStyle;
} else if (accessor.Role == PropertyDeclaration.SetterRole) {
WriteKeyword("set", PropertyDeclaration.SetKeywordRole);
if (accessor.Keyword.Role == PropertyDeclaration.InitKeywordRole) {
WriteKeyword("init", PropertyDeclaration.InitKeywordRole);
} else {
WriteKeyword("set", PropertyDeclaration.SetKeywordRole);
}
style = policy.PropertySetBraceStyle;
} else if (accessor.Role == CustomEventDeclaration.AddAccessorRole) {
WriteKeyword("add", CustomEventDeclaration.AddKeywordRole);

3
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs

@ -33,6 +33,7 @@ using static ICSharpCode.Decompiler.Metadata.MetadataExtensions; @@ -33,6 +33,7 @@ using static ICSharpCode.Decompiler.Metadata.MetadataExtensions;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.DebugInfo;
using System.Collections.Concurrent;
namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
{
@ -205,7 +206,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -205,7 +206,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
var progress = ProgressIndicator;
DecompilerTypeSystem ts = new DecompilerTypeSystem(module, AssemblyResolver, Settings);
Parallel.ForEach(
files,
Partitioner.Create(files, loadBalance: true),
new ParallelOptions {
MaxDegreeOfParallelism = this.MaxDegreeOfParallelism,
CancellationToken = cancellationToken

22
ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs

@ -17,6 +17,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -17,6 +17,7 @@ namespace ICSharpCode.Decompiler.CSharp
static readonly Decompiler.TypeSystem.GenericContext genericContext = default;
readonly HashSet<string> namespaces;
readonly HashSet<IType> visitedTypes = new HashSet<IType>();
public RequiredNamespaceCollector(HashSet<string> namespaces)
{
@ -92,17 +93,18 @@ namespace ICSharpCode.Decompiler.CSharp @@ -92,17 +93,18 @@ namespace ICSharpCode.Decompiler.CSharp
CollectNamespacesForTypeReference(field.ReturnType);
break;
case IMethod method:
HandleAttributes(method.GetAttributes());
HandleAttributes(method.GetReturnTypeAttributes());
CollectNamespacesForTypeReference(method.ReturnType);
foreach (var param in method.Parameters) {
HandleAttributes(param.GetAttributes());
CollectNamespacesForTypeReference(param.Type);
}
HandleTypeParameters(method.TypeParameters);
var reader = module.PEFile.Reader;
var parts = mappingInfo.GetMethodParts((MethodDefinitionHandle)method.MetadataToken).ToList();
foreach (var part in parts) {
var partMethod = module.ResolveMethod(part, genericContext);
HandleAttributes(partMethod.GetAttributes());
HandleAttributes(partMethod.GetReturnTypeAttributes());
CollectNamespacesForTypeReference(partMethod.ReturnType);
foreach (var param in partMethod.Parameters) {
HandleAttributes(param.GetAttributes());
CollectNamespacesForTypeReference(param.Type);
}
HandleTypeParameters(partMethod.TypeParameters);
HandleOverrides(part.GetMethodImplementations(module.metadata), module);
var methodDef = module.metadata.GetMethodDefinition(part);
if (method.HasBody) {
@ -141,13 +143,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -141,13 +143,15 @@ namespace ICSharpCode.Decompiler.CSharp
void CollectNamespacesForTypeReference(IType type)
{
if (!visitedTypes.Add(type))
return;
switch (type) {
case ParameterizedType parameterizedType:
namespaces.Add(parameterizedType.Namespace);
CollectNamespacesForTypeReference(parameterizedType.GenericType);
foreach (var arg in parameterizedType.TypeArguments)
CollectNamespacesForTypeReference(arg);
break;
return; // no need to collect base types again
case TypeWithElementType typeWithElementType:
CollectNamespacesForTypeReference(typeWithElementType.ElementType);
break;

29
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -1002,34 +1002,19 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1002,34 +1002,19 @@ namespace ICSharpCode.Decompiler.CSharp
LocalFunctionDeclarationStatement TranslateFunction(ILFunction function)
{
var stmt = new LocalFunctionDeclarationStatement();
var nestedBuilder = new StatementBuilder(typeSystem, exprBuilder.decompilationContext, function, settings, cancellationToken);
stmt.Name = function.Name;
stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function));
stmt.ReturnType = exprBuilder.ConvertType(function.Method.ReturnType);
stmt.Body = nestedBuilder.ConvertAsBlock(function.Body);
var astBuilder = exprBuilder.astBuilder;
var method = (MethodDeclaration)astBuilder.ConvertEntity(function.ReducedMethod);
method.Body = nestedBuilder.ConvertAsBlock(function.Body);
Comment prev = null;
foreach (string warning in function.Warnings) {
stmt.Body.InsertChildAfter(prev, prev = new Comment(warning), Roles.Comment);
method.Body.InsertChildAfter(prev, prev = new Comment(warning), Roles.Comment);
}
if (function.Method.TypeParameters.Count > 0) {
var astBuilder = exprBuilder.astBuilder;
if (astBuilder.ShowTypeParameters) {
int skipCount = function.ReducedMethod.NumberOfCompilerGeneratedTypeParameters;
stmt.TypeParameters.AddRange(function.Method.TypeParameters.Skip(skipCount).Select(t => astBuilder.ConvertTypeParameter(t)));
if (astBuilder.ShowTypeParameterConstraints) {
stmt.Constraints.AddRange(function.Method.TypeParameters.Skip(skipCount).Select(t => astBuilder.ConvertTypeParameterConstraint(t)).Where(c => c != null));
}
}
}
if (function.IsAsync) {
stmt.Modifiers |= Modifiers.Async;
}
if (settings.StaticLocalFunctions && function.ReducedMethod.IsStaticLocalFunction) {
stmt.Modifiers |= Modifiers.Static;
}
CSharpDecompiler.CleanUpMethodDeclaration(method, method.Body, function);
CSharpDecompiler.RemoveAttribute(method, KnownAttribute.CompilerGenerated);
var stmt = new LocalFunctionDeclarationStatement(method);
stmt.AddAnnotation(new MemberResolveResult(null, function.ReducedMethod));
stmt.WithILInstruction(function);
return stmt;

67
ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs

@ -16,69 +16,22 @@ @@ -16,69 +16,22 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.Collections.Generic;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public class LocalFunctionDeclarationStatement : Statement
{
public AstNodeCollection<TypeParameterDeclaration> TypeParameters {
get { return GetChildrenByRole(Roles.TypeParameter); }
}
public CSharpTokenNode LParToken {
get { return GetChildByRole(Roles.LPar); }
}
public AstNodeCollection<ParameterDeclaration> Parameters {
get { return GetChildrenByRole(Roles.Parameter); }
}
public static readonly Role<MethodDeclaration> MethodDeclarationRole = new Role<MethodDeclaration>("Method");
public CSharpTokenNode RParToken {
get { return GetChildByRole(Roles.RPar); }
public MethodDeclaration Declaration {
get { return GetChildByRole(MethodDeclarationRole); }
set { SetChildByRole(MethodDeclarationRole, value); }
}
public AstNodeCollection<Constraint> Constraints {
get { return GetChildrenByRole(Roles.Constraint); }
}
public BlockStatement Body {
get { return GetChildByRole(Roles.Body); }
set { SetChildByRole(Roles.Body, value); }
}
public Modifiers Modifiers {
get { return EntityDeclaration.GetModifiers(this); }
set { EntityDeclaration.SetModifiers(this, value); }
}
public bool HasModifier(Modifiers mod)
public LocalFunctionDeclarationStatement(MethodDeclaration methodDeclaration)
{
return (Modifiers & mod) == mod;
}
public IEnumerable<CSharpModifierToken> ModifierTokens {
get { return GetChildrenByRole(EntityDeclaration.ModifierRole); }
}
public virtual string Name {
get {
return GetChildByRole(Roles.Identifier).Name;
}
set {
SetChildByRole(Roles.Identifier, Identifier.Create(value, TextLocation.Empty));
}
}
public virtual Identifier NameToken {
get { return GetChildByRole(Roles.Identifier); }
set { SetChildByRole(Roles.Identifier, value); }
}
public virtual AstType ReturnType {
get { return GetChildByRole(Roles.Type); }
set { SetChildByRole(Roles.Type, value); }
AddChild(methodDeclaration, MethodDeclarationRole);
}
public override void AcceptVisitor(IAstVisitor visitor)
@ -98,13 +51,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -98,13 +51,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
protected internal override bool DoMatch(AstNode other, Match match)
{
LocalFunctionDeclarationStatement o = other as LocalFunctionDeclarationStatement;
return o != null && MatchString(this.Name, o.Name)
&& (this.Modifiers == Modifiers.Any || this.Modifiers == o.Modifiers)
&& this.ReturnType.DoMatch(o.ReturnType, match)
&& this.TypeParameters.DoMatch(o.TypeParameters, match)
&& this.Parameters.DoMatch(o.Parameters, match) && this.Constraints.DoMatch(o.Constraints, match)
&& this.Body.DoMatch(o.Body, match);
return other is LocalFunctionDeclarationStatement o && Declaration.DoMatch(o.Declaration, match);
}
}
}

5
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/Accessor.cs

@ -72,13 +72,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -72,13 +72,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
/// <summary>
/// Gets the 'get'/'set'/'add'/'remove' keyword
/// Gets the 'get'/'set'/'init'/'add'/'remove' keyword
/// </summary>
public CSharpTokenNode Keyword {
get {
for (AstNode child = this.FirstChild; child != null; child = child.NextSibling) {
if (child.Role == PropertyDeclaration.GetKeywordRole || child.Role == PropertyDeclaration.SetKeywordRole
|| child.Role == CustomEventDeclaration.AddKeywordRole || child.Role == CustomEventDeclaration.RemoveKeywordRole)
|| child.Role == PropertyDeclaration.InitKeywordRole
|| child.Role == CustomEventDeclaration.AddKeywordRole || child.Role == CustomEventDeclaration.RemoveKeywordRole)
{
return (CSharpTokenNode)child;
}

1
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs

@ -32,6 +32,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -32,6 +32,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public static readonly TokenRole GetKeywordRole = new TokenRole ("get");
public static readonly TokenRole SetKeywordRole = new TokenRole ("set");
public static readonly TokenRole InitKeywordRole = new TokenRole ("init");
public static readonly Role<Accessor> GetterRole = new Role<Accessor>("Getter", Accessor.Null);
public static readonly Role<Accessor> SetterRole = new Role<Accessor>("Setter", Accessor.Null);
public static readonly Role<Expression> ExpressionBodyRole = new Role<Expression>("ExpressionBody", Expression.Null);

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

@ -21,6 +21,7 @@ using System.Collections.Generic; @@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.Semantics;
@ -205,6 +206,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -205,6 +206,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// The default value is <see langword="false" />.
/// </summary>
public bool PrintIntegralValuesAsHex { get; set; }
/// <summary>
/// Controls whether C# 9 "init;" accessors are supported.
/// If disabled, emits "set /*init*/;" instead.
/// </summary>
public bool SupportInitAccessors { get; set; }
#endregion
#region Convert Type
@ -1363,7 +1370,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1363,7 +1370,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return ConvertDestructor((IMethod)entity);
case SymbolKind.Accessor:
IMethod accessor = (IMethod)entity;
return ConvertAccessor(accessor, accessor.AccessorOwner != null ? accessor.AccessorOwner.Accessibility : Accessibility.None, false);
Accessibility ownerAccessibility = accessor.AccessorOwner?.Accessibility ?? Accessibility.None;
return ConvertAccessor(accessor, accessor.AccessorKind, ownerAccessibility, false);
default:
throw new ArgumentException("Invalid value for SymbolKind: " + entity.SymbolKind);
}
@ -1554,15 +1562,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1554,15 +1562,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
}
Accessor ConvertAccessor(IMethod accessor, Accessibility ownerAccessibility, bool addParameterAttribute)
Accessor ConvertAccessor(IMethod accessor, MethodSemanticsAttributes kind, Accessibility ownerAccessibility, bool addParameterAttribute)
{
if (accessor == null)
return Accessor.Null;
Accessor decl = new Accessor();
if (this.ShowAccessibility && accessor.Accessibility != ownerAccessibility)
decl.Modifiers = ModifierFromAccessibility(accessor.Accessibility);
if (accessor.HasReadonlyModifier())
decl.Modifiers |= Modifiers.Readonly;
if (ShowAttributes) {
decl.Attributes.AddRange(ConvertAttributes(accessor.GetAttributes()));
decl.Attributes.AddRange(ConvertAttributes(accessor.GetReturnTypeAttributes(), "return"));
@ -1570,10 +1574,35 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1570,10 +1574,35 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
decl.Attributes.AddRange(ConvertAttributes(accessor.Parameters.Last().GetAttributes(), "param"));
}
}
if (this.ShowAccessibility && accessor.Accessibility != ownerAccessibility)
decl.Modifiers = ModifierFromAccessibility(accessor.Accessibility);
if (accessor.HasReadonlyModifier())
decl.Modifiers |= Modifiers.Readonly;
TokenRole keywordRole = kind switch
{
MethodSemanticsAttributes.Getter => PropertyDeclaration.GetKeywordRole,
MethodSemanticsAttributes.Setter => PropertyDeclaration.SetKeywordRole,
MethodSemanticsAttributes.Adder => CustomEventDeclaration.AddKeywordRole,
MethodSemanticsAttributes.Remover => CustomEventDeclaration.RemoveKeywordRole,
_ => null
};
if (kind == MethodSemanticsAttributes.Setter && SupportInitAccessors && accessor.IsInitOnly) {
keywordRole = PropertyDeclaration.InitKeywordRole;
}
if (keywordRole != null) {
decl.AddChild(new CSharpTokenNode(TextLocation.Empty, keywordRole), keywordRole);
}
if (accessor.IsInitOnly && keywordRole != PropertyDeclaration.InitKeywordRole) {
decl.AddChild(new Comment("init", CommentType.MultiLine), Roles.Comment);
}
if (AddResolveResultAnnotations) {
decl.AddAnnotation(new MemberResolveResult(null, accessor));
}
decl.Body = GenerateBodyBlock();
if (GenerateBody) {
decl.Body = GenerateBodyBlock();
} else {
decl.AddChild(new CSharpTokenNode(TextLocation.Empty, Roles.Semicolon), Roles.Semicolon);
}
return decl;
}
@ -1592,8 +1621,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1592,8 +1621,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
ct.HasReadOnlySpecifier = true;
}
decl.Name = property.Name;
decl.Getter = ConvertAccessor(property.Getter, property.Accessibility, false);
decl.Setter = ConvertAccessor(property.Setter, property.Accessibility, true);
decl.Getter = ConvertAccessor(property.Getter, MethodSemanticsAttributes.Getter, property.Accessibility, false);
decl.Setter = ConvertAccessor(property.Setter, MethodSemanticsAttributes.Setter, property.Accessibility, true);
decl.PrivateImplementationType = GetExplicitInterfaceType (property);
MergeReadOnlyModifiers(decl, decl.Getter, decl.Setter);
return decl;
@ -1624,8 +1653,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1624,8 +1653,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
foreach (IParameter p in indexer.Parameters) {
decl.Parameters.Add(ConvertParameter(p));
}
decl.Getter = ConvertAccessor(indexer.Getter, indexer.Accessibility, false);
decl.Setter = ConvertAccessor(indexer.Setter, indexer.Accessibility, true);
decl.Getter = ConvertAccessor(indexer.Getter, MethodSemanticsAttributes.Getter, indexer.Accessibility, false);
decl.Setter = ConvertAccessor(indexer.Setter, MethodSemanticsAttributes.Setter, indexer.Accessibility, true);
decl.PrivateImplementationType = GetExplicitInterfaceType (indexer);
MergeReadOnlyModifiers(decl, decl.Getter, decl.Setter);
return decl;
@ -1644,8 +1673,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1644,8 +1673,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
decl.ReturnType = ConvertType(ev.ReturnType);
decl.Name = ev.Name;
decl.AddAccessor = ConvertAccessor(ev.AddAccessor, ev.Accessibility, true);
decl.RemoveAccessor = ConvertAccessor(ev.RemoveAccessor, ev.Accessibility, true);
decl.AddAccessor = ConvertAccessor(ev.AddAccessor, MethodSemanticsAttributes.Adder, ev.Accessibility, true);
decl.RemoveAccessor = ConvertAccessor(ev.RemoveAccessor, MethodSemanticsAttributes.Remover, ev.Accessibility, true);
decl.PrivateImplementationType = GetExplicitInterfaceType (ev);
MergeReadOnlyModifiers(decl, decl.AddAccessor, decl.RemoveAccessor);
return decl;
@ -1799,6 +1828,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1799,6 +1828,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return !member.IsStatic;
case SymbolKind.Destructor:
return false;
case SymbolKind.Method:
return !((IMethod)member).IsLocalFunction;
default:
return true;
}
@ -1811,7 +1842,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1811,7 +1842,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
m |= ModifierFromAccessibility (member.Accessibility);
}
if (this.ShowModifiers) {
if (member.IsStatic) {
if (member is LocalFunctionMethod localFunction) {
if (localFunction.IsStaticLocalFunction) {
m |= Modifiers.Static;
}
} else if (member.IsStatic) {
m |= Modifiers.Static;
} else {
var declaringType = member.DeclaringType;

20
ICSharpCode.Decompiler/CSharp/Transforms/ContextTrackingVisitor.cs

@ -55,56 +55,56 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -55,56 +55,56 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
public override TResult VisitMethodDeclaration(MethodDeclaration methodDeclaration)
{
Debug.Assert(currentMethod == null);
var oldMethod = currentMethod;
try {
currentMethod = methodDeclaration.GetSymbol() as IMethod;
return base.VisitMethodDeclaration(methodDeclaration);
} finally {
currentMethod = null;
currentMethod = oldMethod;
}
}
public override TResult VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration)
{
Debug.Assert(currentMethod == null);
var oldMethod = currentMethod;
try {
currentMethod = constructorDeclaration.GetSymbol() as IMethod;
return base.VisitConstructorDeclaration(constructorDeclaration);
} finally {
currentMethod = null;
currentMethod = oldMethod;
}
}
public override TResult VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration)
{
Debug.Assert(currentMethod == null);
var oldMethod = currentMethod;
try {
currentMethod = destructorDeclaration.GetSymbol() as IMethod;
return base.VisitDestructorDeclaration(destructorDeclaration);
} finally {
currentMethod = null;
currentMethod = oldMethod;
}
}
public override TResult VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration)
{
Debug.Assert(currentMethod == null);
var oldMethod = currentMethod;
try {
currentMethod = operatorDeclaration.GetSymbol() as IMethod;
return base.VisitOperatorDeclaration(operatorDeclaration);
} finally {
currentMethod = null;
currentMethod = oldMethod;
}
}
public override TResult VisitAccessor(Accessor accessor)
{
Debug.Assert(currentMethod == null);
var oldMethod = currentMethod;
try {
currentMethod = accessor.GetSymbol() as IMethod;
return base.VisitAccessor(accessor);
} finally {
currentMethod = null;
currentMethod = oldMethod;
}
}
}

20
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -116,12 +116,13 @@ namespace ICSharpCode.Decompiler @@ -116,12 +116,13 @@ namespace ICSharpCode.Decompiler
}
if (languageVersion < CSharp.LanguageVersion.Preview) {
nativeIntegers = false;
initAccessors = false;
}
}
public CSharp.LanguageVersion GetMinimumRequiredVersion()
{
if (nativeIntegers)
if (nativeIntegers || initAccessors)
return CSharp.LanguageVersion.Preview;
if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement || staticLocalFunctions || ranges)
return CSharp.LanguageVersion.CSharp8_0;
@ -163,6 +164,23 @@ namespace ICSharpCode.Decompiler @@ -163,6 +164,23 @@ namespace ICSharpCode.Decompiler
}
}
bool initAccessors = true;
/// <summary>
/// Use C# 9 <c>init;</c> property accessors.
/// </summary>
[Category("C# 9.0 (experimental)")]
[Description("DecompilerSettings.InitAccessors")]
public bool InitAccessors {
get { return initAccessors; }
set {
if (initAccessors != value) {
initAccessors = value;
OnPropertyChanged();
}
}
}
bool anonymousMethods = true;
/// <summary>

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

@ -33,9 +33,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -33,9 +33,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// <summary>
/// Decompiler step for C# 5 async/await.
/// </summary>
class AsyncAwaitDecompiler : IILTransform
public class AsyncAwaitDecompiler : IILTransform
{
public static bool IsCompilerGeneratedStateMachine(TypeDefinitionHandle type, MetadataReader metadata)
internal static bool IsCompilerGeneratedStateMachine(TypeDefinitionHandle type, MetadataReader metadata)
{
TypeDefinition td;
if (type.IsNil || (td = metadata.GetTypeDefinition(type)).GetDeclaringType().IsNil)
@ -50,7 +50,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -50,7 +50,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return false;
}
public static bool IsCompilerGeneratedMainMethod(Metadata.PEFile module, MethodDefinitionHandle method)
internal static bool IsCompilerGeneratedMainMethod(Metadata.PEFile module, MethodDefinitionHandle method)
{
var metadata = module.Metadata;
var definition = metadata.GetMethodDefinition(method);

27
ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs

@ -200,11 +200,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -200,11 +200,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// * use only a single exit point if at all possible
/// * minimize the amount of code in the in-loop partition
/// (thus: maximize the amount of code in the out-of-loop partition)
/// "amount of code" could be measured as:
/// * number of basic blocks
/// * number of instructions directly in those basic blocks (~= number of statements)
/// * number of instructions in those basic blocks (~= number of expressions)
/// (we currently use the number of statements)
///
/// Observations:
/// * If a node is in-loop, so are all its ancestors in the dominator tree (up to the loop entry point)
@ -315,6 +310,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -315,6 +310,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
ControlFlowNode exitPoint = null;
int exitPointILOffset = -1;
ConsiderReturnAsExitPoint((Block)loopHead.UserData, ref exitPoint, ref exitPointILOffset);
foreach (var node in loopHead.DominatorTreeChildren) {
PickExitPoint(node, ref exitPoint, ref exitPointILOffset);
}
@ -457,12 +453,31 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -457,12 +453,31 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
exitPoint = node;
exitPointILOffset = block.StartILOffset;
return; // don't visit children, they are likely to have even later IL offsets and we'd end up
// moving almost all of the code into the loop.
// moving almost all of the code into the loop.
}
ConsiderReturnAsExitPoint(block, ref exitPoint, ref exitPointILOffset);
foreach (var child in node.DominatorTreeChildren) {
PickExitPoint(child, ref exitPoint, ref exitPointILOffset);
}
}
private static void ConsiderReturnAsExitPoint(Block block, ref ControlFlowNode exitPoint, ref int exitPointILOffset)
{
// It's possible that the real exit point of the loop is a "return;" that has been combined (by ControlFlowSimplification)
// with the condition block.
if (!block.MatchIfAtEndOfBlock(out _, out var trueInst, out var falseInst))
return;
if (trueInst.StartILOffset > exitPointILOffset && trueInst is Leave { IsLeavingFunction: true, Value: Nop _ }) {
// By using NoExitPoint, everything (including the "return;") becomes part of the loop body
// Then DetectExitPoint will move the "return;" out of the loop body.
exitPoint = NoExitPoint;
exitPointILOffset = trueInst.StartILOffset;
}
if (falseInst.StartILOffset > exitPointILOffset && falseInst is Leave { IsLeavingFunction: true, Value: Nop _ }) {
exitPoint = NoExitPoint;
exitPointILOffset = falseInst.StartILOffset;
}
}
/// <summary>
/// Constructs a new control flow graph.

2
ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs

@ -35,7 +35,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -35,7 +35,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
/// Detect sequences of conditional branches that all test a single integer value,
/// and simplify them into a ILAst switch instruction (which like C# does not require contiguous values).
/// </summary>
class SwitchDetection : IILTransform
public class SwitchDetection : IILTransform
{
private readonly SwitchAnalysis analysis = new SwitchAnalysis();

2
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -28,7 +28,7 @@ using System.Reflection.Metadata; @@ -28,7 +28,7 @@ using System.Reflection.Metadata;
namespace ICSharpCode.Decompiler.IL.ControlFlow
{
class YieldReturnDecompiler : IILTransform
public class YieldReturnDecompiler : IILTransform
{
// For a description on the code generated by the C# compiler for yield return:
// http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx

8
ICSharpCode.Decompiler/IL/ILReader.cs

@ -515,10 +515,10 @@ namespace ICSharpCode.Decompiler.IL @@ -515,10 +515,10 @@ namespace ICSharpCode.Decompiler.IL
var blockBuilder = new BlockBuilder(body, variableByExceptionHandler);
blockBuilder.CreateBlocks(mainContainer, instructionBuilder, isBranchTarget, cancellationToken);
var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer, kind);
CollectionExtensions.AddRange(function.Variables, parameterVariables);
CollectionExtensions.AddRange(function.Variables, localVariables);
CollectionExtensions.AddRange(function.Variables, stackVariables);
CollectionExtensions.AddRange(function.Variables, variableByExceptionHandler.Values);
function.Variables.AddRange(parameterVariables);
function.Variables.AddRange(localVariables);
function.Variables.AddRange(stackVariables);
function.Variables.AddRange(variableByExceptionHandler.Values);
function.AddRef(); // mark the root node
var removedBlocks = new List<Block>();
foreach (var c in function.Descendants.OfType<BlockContainer>()) {

14
ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs

@ -20,6 +20,7 @@ using System; @@ -20,6 +20,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL
@ -239,6 +240,19 @@ namespace ICSharpCode.Decompiler.IL @@ -239,6 +240,19 @@ namespace ICSharpCode.Decompiler.IL
}
}
internal override bool CanInlineIntoSlot(int childIndex, ILInstruction expressionBeingMoved)
{
// Inlining into the entry-point is allowed as long as we're not moving code into a loop.
// This is used to inline into the switch expression.
return childIndex == 0 && this.EntryPoint.IncomingEdgeCount == 1;
}
internal override bool PrepareExtract(int childIndex, ExtractionContext ctx)
{
// Un-inlining from the entry-point is allowed as long as we're not moving code out of a loop
return childIndex == 0 && this.EntryPoint.IncomingEdgeCount == 1;
}
/// <summary>
/// Topologically sort the blocks.
/// The new order is returned without modifying the BlockContainer.

1
ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using ICSharpCode.Decompiler.IL.Patterns;
using ICSharpCode.Decompiler.TypeSystem;

11
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -358,7 +358,16 @@ namespace ICSharpCode.Decompiler.IL @@ -358,7 +358,16 @@ namespace ICSharpCode.Decompiler.IL
return InstructionFlags.MayThrow | InstructionFlags.ControlFlow;
}
}
internal override bool CanInlineIntoSlot(int childIndex, ILInstruction expressionBeingMoved)
{
// With expression trees, we occasionally need to inline constants into an existing expression tree.
// Only allow this for completely pure constants; a MayReadLocals effect would already be problematic
// because we're essentially delaying evaluation of the expression until the ILFunction is called.
Debug.Assert(childIndex == 0);
return kind == ILFunctionKind.ExpressionTree && expressionBeingMoved.Flags == InstructionFlags.None;
}
/// <summary>
/// Apply a list of transforms to this function.
/// </summary>

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

@ -802,6 +802,15 @@ namespace ICSharpCode.Decompiler.IL @@ -802,6 +802,15 @@ namespace ICSharpCode.Decompiler.IL
}
return true;
}
/// <summary>
/// Gets whether the specified instruction may be inlined into the specified slot.
/// Note: this does not check whether reordering with the previous slots is valid; only wheter the target slot supports inlining at all!
/// </summary>
internal virtual bool CanInlineIntoSlot(int childIndex, ILInstruction expressionBeingMoved)
{
return GetChildSlot(childIndex).CanInlineInto;
}
}
public interface IInstructionWithTypeOperand

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

@ -63,7 +63,7 @@ namespace ICSharpCode.Decompiler.IL @@ -63,7 +63,7 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public bool RefOutput { get => ResultType == StackType.Ref; }
public NullableUnwrap(StackType unwrappedType, ILInstruction argument, bool refInput=false)
public NullableUnwrap(StackType unwrappedType, ILInstruction argument, bool refInput = false)
: base(OpCode.NullableUnwrap, argument)
{
this.ResultType = unwrappedType;
@ -132,5 +132,13 @@ namespace ICSharpCode.Decompiler.IL @@ -132,5 +132,13 @@ namespace ICSharpCode.Decompiler.IL
return base.PrepareExtract(childIndex, ctx)
&& (ctx.FlagsBeingMoved & InstructionFlags.MayUnwrapNull) == 0;
}
internal override bool CanInlineIntoSlot(int childIndex, ILInstruction expressionBeingMoved)
{
// Inlining into nullable.rewrap is OK unless the expression being inlined
// contains a nullable.wrap that isn't being re-wrapped within the expression being inlined.
return base.CanInlineIntoSlot(childIndex, expressionBeingMoved)
&& !expressionBeingMoved.HasFlag(InstructionFlags.MayUnwrapNull);
}
}
}

24
ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs

@ -20,6 +20,8 @@ using System; @@ -20,6 +20,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using Humanizer;
using ICSharpCode.Decompiler.CSharp.OutputVisitor;
using ICSharpCode.Decompiler.TypeSystem;
@ -51,6 +53,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -51,6 +53,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILTransformContext context;
string[] currentFieldNames;
Dictionary<string, int> reservedVariableNames;
Dictionary<MethodDefinitionHandle, string> localFunctionMapping;
HashSet<ILVariable> loopCounters;
const char maxLoopVariableName = 'n';
@ -59,6 +62,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -59,6 +62,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
this.context = context;
currentFieldNames = function.Method.DeclaringTypeDefinition.Fields.Select(f => f.Name).ToArray();
reservedVariableNames = new Dictionary<string, int>();
localFunctionMapping = new Dictionary<MethodDefinitionHandle, string>();
loopCounters = CollectLoopCounters(function);
foreach (var f in function.Descendants.OfType<ILFunction>()) {
if (f.Method != null) {
@ -180,6 +184,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -180,6 +184,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!LocalFunctionDecompiler.ParseLocalFunctionName(localFunction.Name, out _, out var newName) || !IsValidName(newName))
newName = null;
localFunction.Name = newName;
localFunction.ReducedMethod.Name = newName;
}
// Now generate names:
var mapping = new Dictionary<ILVariable, string>(ILVariableEqualityComparer.Instance);
@ -199,6 +204,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -199,6 +204,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms
newName = GetAlternativeName("f");
}
localFunction.Name = newName;
localFunction.ReducedMethod.Name = newName;
localFunctionMapping[(MethodDefinitionHandle)localFunction.ReducedMethod.MetadataToken] = newName;
}
foreach (var inst in function.Descendants) {
LocalFunctionMethod localFunction;
switch (inst) {
case Call call:
localFunction = call.Method as LocalFunctionMethod;
break;
case LdFtn ldftn:
localFunction = ldftn.Method as LocalFunctionMethod;
break;
default:
localFunction = null;
break;
}
if (localFunction == null || !localFunctionMapping.TryGetValue((MethodDefinitionHandle)localFunction.MetadataToken, out var name))
continue;
localFunction.Name = name;
}
}

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

@ -138,7 +138,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -138,7 +138,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
};
block.Instructions.Insert(i++, new StLoc(uninlinedArgs[j], arg));
}
CollectionExtensions.AddRange(v.Function.Variables, uninlinedArgs);
v.Function.Variables.AddRange(uninlinedArgs);
// perform copy propagation:
foreach (var expr in v.LoadInstructions.ToArray()) {
var clone = copiedExpr.Clone();

4
ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs

@ -25,9 +25,9 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -25,9 +25,9 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
/// <summary>
///
/// Transforms anonymous methods and lambdas by creating nested ILFunctions.
/// </summary>
class DelegateConstruction : IILTransform
public class DelegateConstruction : IILTransform
{
ILTransformContext context;
ITypeResolveContext decompilationContext;

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

@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
class EarlyExpressionTransforms : ILVisitor, IILTransform
public class EarlyExpressionTransforms : ILVisitor, IILTransform
{
ILTransformContext context;

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

@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// This transform un-inlines the argument of `isinst` instructions that can't be directly translated to C#,
/// thus allowing the emulation via "expr is T ? (T)expr : null".
/// </summary>
class FixLoneIsInst : IILTransform
public class FixLoneIsInst : IILTransform
{
void IILTransform.Run(ILFunction function, ILTransformContext context)
{

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

@ -589,6 +589,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -589,6 +589,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} else if (expr is Block block) {
// Inlining into inline-blocks?
switch (block.Kind) {
case BlockKind.ControlFlow when block.Parent is BlockContainer:
case BlockKind.ArrayInitializer:
case BlockKind.CollectionInitializer:
case BlockKind.ObjectInitializer:
@ -604,19 +605,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -604,19 +605,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
default:
return FindResult.Stop;
}
} else if (expr is BlockContainer container && container.EntryPoint.IncomingEdgeCount == 1) {
// Possibly a switch-container, allow inlining into the switch instruction:
return NoContinue(FindLoadInNext(container.EntryPoint.Instructions[0], v, expressionBeingMoved, options));
// If FindLoadInNext() returns null, we still can't continue searching
// because we can't inline over the remainder of the blockcontainer.
} else if (expr is NullableRewrap) {
// Inlining into nullable.rewrap is OK unless the expression being inlined
// contains a nullable.wrap that isn't being re-wrapped within the expression being inlined.
if (expressionBeingMoved.HasFlag(InstructionFlags.MayUnwrapNull))
return FindResult.Stop;
}
foreach (var child in expr.Children) {
if (!child.SlotInfo.CanInlineInto)
if (!expr.CanInlineIntoSlot(child.ChildIndex, expressionBeingMoved))
return FindResult.Stop;
// Recursively try to find the load instruction
@ -666,24 +657,33 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -666,24 +657,33 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
/// <summary>
/// Gets whether arg can be un-inlined out of stmt.
/// Gets whether 'expressionBeingMoved' can be moved from somewhere before 'stmt' to become the replacement of 'targetLoad'.
/// </summary>
/// <seealso cref="ILInstruction.Extract"/>
internal static bool CanUninline(ILInstruction arg, ILInstruction stmt)
public static bool CanMoveInto(ILInstruction expressionBeingMoved, ILInstruction stmt, ILInstruction targetLoad)
{
Debug.Assert(arg.IsDescendantOf(stmt));
for (ILInstruction inst = arg; inst != stmt; inst = inst.Parent) {
if (!inst.SlotInfo.CanInlineInto)
Debug.Assert(targetLoad.IsDescendantOf(stmt));
for (ILInstruction inst = targetLoad; inst != stmt; inst = inst.Parent) {
if (!inst.Parent.CanInlineIntoSlot(inst.ChildIndex, expressionBeingMoved))
return false;
// Check whether re-ordering with predecessors is valid:
int childIndex = inst.ChildIndex;
for (int i = 0; i < childIndex; ++i) {
ILInstruction predecessor = inst.Parent.Children[i];
if (!SemanticHelper.MayReorder(arg, predecessor))
if (!IsSafeForInlineOver(predecessor, expressionBeingMoved))
return false;
}
}
return true;
}
/// <summary>
/// Gets whether arg can be un-inlined out of stmt.
/// </summary>
/// <seealso cref="ILInstruction.Extract"/>
internal static bool CanUninline(ILInstruction arg, ILInstruction stmt)
{
// moving into and moving out-of are equivalent
return CanMoveInto(arg, stmt, arg);
}
}
}

19
ICSharpCode.Decompiler/IL/Transforms/IndexRangeTransform.cs

@ -205,6 +205,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -205,6 +205,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!CheckContainerLengthVariableUseCount(containerLengthVar, startIndexKind)) {
return;
}
if (!call.IsDescendantOf(block.Instructions[pos]))
return;
// startOffsetVar might be used deep inside a complex statement, ensure we can inline up to that point:
for (int i = startPos; i < pos; i++) {
if (!ILInlining.CanInlineInto(block.Instructions[pos], startOffsetVar, block.Instructions[i]))
@ -271,13 +273,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -271,13 +273,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return;
}
if (rangeVar != null) {
if (!MatchIndexFromRange(startIndexKind, startIndexLoad, rangeVar, "get_Start"))
return;
if (!MatchIndexFromRange(endIndexKind, endIndexLoad, rangeVar, "get_End"))
return;
return; // this should only ever happen in the second step (ExtendSlicing)
}
if (!(sliceLengthVar.LoadInstructions.Single().Parent is CallInstruction call))
return;
if (!call.IsDescendantOf(block.Instructions[pos]))
return;
if (!IsSlicingMethod(call.Method))
return;
if (call.Arguments.Count != 3)
@ -291,9 +292,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -291,9 +292,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} else {
if (!call.Arguments[1].MatchLdLoc(startOffsetVar))
return;
if (!ILInlining.CanMoveInto(startOffsetVarInit, block.Instructions[pos], call.Arguments[1]))
return;
}
if (!call.Arguments[2].MatchLdLoc(sliceLengthVar))
return;
if (!ILInlining.CanMoveInto(sliceLengthVarInit, block.Instructions[pos], call.Arguments[2]))
return;
if (!CSharpWillGenerateIndexer(call.Method.DeclaringType, slicing: true))
return;
var specialMethods = new IndexMethods(context.TypeSystem);
@ -385,13 +390,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -385,13 +390,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// holds because we've used containerLengthVar at least once
Debug.Assert(startIndexKind != IndexKind.FromStart || endIndexKind != IndexKind.FromStart);
if (rangeVar != null) {
if (!ILInlining.CanMoveInto(rangeVarInit, block.Instructions[pos], startIndexLoad))
return;
if (!MatchIndexFromRange(startIndexKind, startIndexLoad, rangeVar, "get_Start"))
return;
if (!MatchIndexFromRange(endIndexKind, endIndexLoad, rangeVar, "get_End"))
return;
}
context.Step("Merge containerLengthVar into slicing", slicingCall);
var specialMethods = new IndexMethods(context.TypeSystem);
if (!specialMethods.IsValid)
return;
context.Step("Merge containerLengthVar into slicing", slicingCall);
rangeCtorCall.ReplaceWith(MakeRange(startIndexKind, startIndexLoad, endIndexKind, endIndexLoad, specialMethods));
for (int i = startPos; i < pos; i++) {
slicingCall.AddILRange(block.Instructions[i]);

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

@ -24,7 +24,7 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -24,7 +24,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
class IntroduceDynamicTypeOnLocals : IILTransform
public class IntroduceDynamicTypeOnLocals : IILTransform
{
public void Run(ILFunction function, ILTransformContext context)
{

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

@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
class IntroduceRefReadOnlyModifierOnLocals : IILTransform
public class IntroduceRefReadOnlyModifierOnLocals : IILTransform
{
public void Run(ILFunction function, ILTransformContext context)
{

24
ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs

@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary>
/// Decompiler step for C# 7.0 local functions
/// </summary>
class LocalFunctionDecompiler : IILTransform
public class LocalFunctionDecompiler : IILTransform
{
ILTransformContext context;
ITypeResolveContext resolveContext;
@ -484,7 +484,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -484,7 +484,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
break;
parametersToRemove++;
}
return new LocalFunctionMethod(method, parametersToRemove, typeParametersToRemove);
return new LocalFunctionMethod(method, method.Name, CanBeStaticLocalFunction(), parametersToRemove, typeParametersToRemove);
bool CanBeStaticLocalFunction()
{
if (!context.Settings.StaticLocalFunctions)
return false;
// Cannot be static because there are closure parameters that will be removed
if (parametersToRemove > 0)
return false;
// no closure parameters, but static:
// we can safely assume, this local function can be declared static
if (method.IsStatic)
return true;
// the local function is used in conjunction with a lambda, which means,
// it is defined inside the display-class type
var declaringType = method.DeclaringTypeDefinition;
if (!declaringType.IsCompilerGenerated())
return false;
// if there are no instance fields, we can make it a static local function
return !declaringType.GetFields(f => !f.IsStatic).Any();
}
}
static void TransformToLocalFunctionReference(ILFunction function, CallInstruction useSite)

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

@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
class LockTransform : IBlockTransform
public class LockTransform : IBlockTransform
{
BlockTransformContext context;

4
ICSharpCode.Decompiler/IL/Transforms/NamedArgumentTransform.cs

@ -10,9 +10,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -10,9 +10,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
using FindResult = ILInlining.FindResult;
using FindResultType = ILInlining.FindResultType;
class NamedArgumentTransform : IStatementTransform
public class NamedArgumentTransform : IStatementTransform
{
public static FindResult CanIntroduceNamedArgument(CallInstruction call, ILInstruction child, ILVariable v, ILInstruction expressionBeingMoved)
internal static FindResult CanIntroduceNamedArgument(CallInstruction call, ILInstruction child, ILVariable v, ILInstruction expressionBeingMoved)
{
Debug.Assert(child.Parent == call);
if (call.IsInstanceCall && child.ChildIndex == 0)

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

@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
///
/// The ?? operator for nullable value types is handled by NullableLiftingTransform.
/// </summary>
class NullCoalescingTransform : IStatementTransform
public class NullCoalescingTransform : IStatementTransform
{
public void Run(Block block, int pos, StatementTransformContext context)
{

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

@ -489,7 +489,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -489,7 +489,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
class NullPropagationStatementTransform : IStatementTransform
public class NullPropagationStatementTransform : IStatementTransform
{
public void Run(Block block, int pos, StatementTransformContext context)
{

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

@ -908,7 +908,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -908,7 +908,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
#endregion
}
class NullableLiftingStatementTransform : IStatementTransform
public class NullableLiftingStatementTransform : IStatementTransform
{
public void Run(Block block, int pos, StatementTransformContext context)
{

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

@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary>
/// Detects switch-on-nullable patterns employed by the C# compiler and transforms them to an ILAst-switch-instruction.
/// </summary>
class SwitchOnNullableTransform : IILTransform
public class SwitchOnNullableTransform : IILTransform
{
public void Run(ILFunction function, ILTransformContext context)
{

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

@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary>
/// Detects switch-on-string patterns employed by the C# compiler and transforms them to an ILAst-switch-instruction.
/// </summary>
class SwitchOnStringTransform : IILTransform
public class SwitchOnStringTransform : IILTransform
{
ILTransformContext context;

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

@ -43,7 +43,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -43,7 +43,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Note that 2) and 3) apply because declarations and uses of lambdas and local functions
/// are already transformed by the time this transform is applied.
/// </summary>
class TransformDisplayClassUsage : ILVisitor, IILTransform
public class TransformDisplayClassUsage : ILVisitor, IILTransform
{
class VariableToDeclare
{

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

@ -26,7 +26,7 @@ using ICSharpCode.Decompiler.Util; @@ -26,7 +26,7 @@ using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL.Transforms
{
class UserDefinedLogicTransform : IStatementTransform
public class UserDefinedLogicTransform : IStatementTransform
{
void IStatementTransform.Run(Block block, int pos, StatementTransformContext context)
{

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

@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
class UsingTransform : IBlockTransform
public class UsingTransform : IBlockTransform
{
BlockTransformContext context;

55
ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs

@ -61,29 +61,30 @@ namespace ICSharpCode.Decompiler.Metadata @@ -61,29 +61,30 @@ namespace ICSharpCode.Decompiler.Metadata
};
readonly DotNetCorePackageInfo[] packages;
ISet<string> packageBasePaths = new HashSet<string>(StringComparer.Ordinal);
readonly Version version;
readonly List<string> searchPaths = new List<string>();
readonly List<string> packageBasePaths = new List<string>();
readonly Version targetFrameworkVersion;
readonly string dotnetBasePath = FindDotNetExeDirectory();
public DotNetCorePathFinder(Version version)
public DotNetCorePathFinder(TargetFrameworkIdentifier targetFramework, Version targetFrameworkVersion)
{
this.version = version;
}
public DotNetCorePathFinder(string parentAssemblyFileName, string targetFrameworkIdString, TargetFrameworkIdentifier targetFramework, Version version, ReferenceLoadInfo loadInfo = null)
{
string assemblyName = Path.GetFileNameWithoutExtension(parentAssemblyFileName);
string basePath = Path.GetDirectoryName(parentAssemblyFileName);
this.version = version;
this.targetFrameworkVersion = targetFrameworkVersion;
if (targetFramework == TargetFrameworkIdentifier.NETStandard) {
// .NET Standard 2.1 is implemented by .NET Core 3.0 or higher
if (version.Major == 2 && version.Minor == 1) {
this.version = new Version(3, 0, 0);
if (targetFrameworkVersion.Major == 2 && targetFrameworkVersion.Minor == 1) {
this.targetFrameworkVersion = new Version(3, 0, 0);
}
}
}
public DotNetCorePathFinder(string parentAssemblyFileName, string targetFrameworkIdString, TargetFrameworkIdentifier targetFramework, Version targetFrameworkVersion, ReferenceLoadInfo loadInfo = null)
: this(targetFramework, targetFrameworkVersion)
{
string assemblyName = Path.GetFileNameWithoutExtension(parentAssemblyFileName);
string basePath = Path.GetDirectoryName(parentAssemblyFileName);
packageBasePaths.Add(basePath);
searchPaths.Add(basePath);
var depsJsonFileName = Path.Combine(basePath, $"{assemblyName}.deps.json");
if (File.Exists(depsJsonFileName)) {
@ -106,17 +107,17 @@ namespace ICSharpCode.Decompiler.Metadata @@ -106,17 +107,17 @@ namespace ICSharpCode.Decompiler.Metadata
public void AddSearchDirectory(string path)
{
this.packageBasePaths.Add(path);
this.searchPaths.Add(path);
}
public void RemoveSearchDirectory(string path)
{
this.packageBasePaths.Remove(path);
this.searchPaths.Remove(path);
}
public string TryResolveDotNetCore(IAssemblyReference name)
{
foreach (var basePath in packageBasePaths) {
foreach (var basePath in searchPaths.Concat(packageBasePaths)) {
if (File.Exists(Path.Combine(basePath, name.Name + ".dll"))) {
return Path.Combine(basePath, name.Name + ".dll");
} else if (File.Exists(Path.Combine(basePath, name.Name + ".exe"))) {
@ -124,7 +125,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -124,7 +125,7 @@ namespace ICSharpCode.Decompiler.Metadata
}
}
return FallbackToDotNetSharedDirectory(name, version);
return FallbackToDotNetSharedDirectory(name);
}
internal string GetReferenceAssemblyPath(string targetFramework)
@ -169,7 +170,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -169,7 +170,7 @@ namespace ICSharpCode.Decompiler.Metadata
}
}
string FallbackToDotNetSharedDirectory(IAssemblyReference name, Version version)
string FallbackToDotNetSharedDirectory(IAssemblyReference name)
{
if (dotnetBasePath == null)
return null;
@ -177,7 +178,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -177,7 +178,7 @@ namespace ICSharpCode.Decompiler.Metadata
foreach (var basePath in basePaths) {
if (!Directory.Exists(basePath))
continue;
var closestVersion = GetClosestVersionFolder(basePath, version);
var closestVersion = GetClosestVersionFolder(basePath, targetFrameworkVersion);
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"))) {
@ -189,15 +190,17 @@ namespace ICSharpCode.Decompiler.Metadata @@ -189,15 +190,17 @@ namespace ICSharpCode.Decompiler.Metadata
static string GetClosestVersionFolder(string basePath, Version version)
{
string result = null;
foreach (var folder in new DirectoryInfo(basePath).GetDirectories().Select(d => ConvertToVersion(d.Name)).Where(v => v.Item1 != null).OrderByDescending(v => v.Item1)) {
if (folder.Item1 >= version)
result = folder.Item2;
var foundVersions = new DirectoryInfo(basePath).GetDirectories()
.Select(d => ConvertToVersion(d.Name))
.Where(v => v.version != null);
foreach (var folder in foundVersions.OrderBy(v => v.Item1)) {
if (folder.version >= version)
return folder.directoryName;
}
return result ?? version.ToString();
return version.ToString();
}
internal static (Version, string) ConvertToVersion(string name)
internal static (Version version, string directoryName) ConvertToVersion(string name)
{
string RemoveTrailingVersionInfo()
{

33
ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs

@ -20,20 +20,26 @@ using System; @@ -20,20 +20,26 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.Metadata
{
public static class DotNetCorePathFinderExtensions
{
static readonly string RefPathPattern =
static readonly string PathPattern =
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<type>.NETFramework)[/\\]v(?<version>[^/\\]+)[/\\])" +
@"|((?<type>Microsoft\.NET)[/\\]assembly[/\\]GAC_(MSIL|32|64)[/\\])" +
@"|((?<type>Microsoft\.NET)[/\\]Framework(64)?[/\\](?<version>[^/\\]+)[/\\])" +
@"|(NuGetFallbackFolder[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\]ref[/\\])" +
@"|(shared[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\])";
@"|(shared[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\])" +
@"|(packs[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)\\ref([/\\].*)?[/\\])";
static readonly string RefPathPattern =
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<type>.NETFramework)[/\\]v(?<version>[^/\\]+)[/\\])" +
@"|(NuGetFallbackFolder[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\]ref[/\\])" +
@"|(packs[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)\\ref([/\\].*)?[/\\])";
public static string DetectTargetFrameworkId(this PEFile assembly)
{
@ -99,7 +105,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -99,7 +105,7 @@ namespace ICSharpCode.Decompiler.Metadata
* - .NETCore -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Console.dll
* - .NETStandard -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll
*/
var pathMatch = Regex.Match(assemblyPath, RefPathPattern,
var pathMatch = Regex.Match(assemblyPath, PathPattern,
RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture);
if (pathMatch.Success) {
var type = pathMatch.Groups["type"].Value;
@ -121,6 +127,25 @@ namespace ICSharpCode.Decompiler.Metadata @@ -121,6 +127,25 @@ namespace ICSharpCode.Decompiler.Metadata
return string.Empty;
}
public static bool IsReferenceAssembly(this PEFile assembly)
{
return IsReferenceAssembly(assembly.Reader, assembly.FileName);
}
public static bool IsReferenceAssembly(this PEReader assembly, string assemblyPath)
{
if (assembly == null)
throw new ArgumentNullException(nameof(assembly));
var metadata = assembly.GetMetadataReader();
if (metadata.GetCustomAttributes(Handle.AssemblyDefinition).HasKnownAttribute(metadata, KnownAttribute.ReferenceAssembly))
return true;
// Try to detect reference assembly through specific path pattern
var refPathMatch = Regex.Match(assemblyPath, RefPathPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
return refPathMatch.Success;
}
}
public class ReferenceLoadInfo

4
ICSharpCode.Decompiler/Metadata/PEFile.cs

@ -75,8 +75,12 @@ namespace ICSharpCode.Decompiler.Metadata @@ -75,8 +75,12 @@ namespace ICSharpCode.Decompiler.Metadata
public TargetRuntime GetRuntime()
{
string version = Metadata.MetadataVersion;
if (version == null || version.Length <= 1)
return TargetRuntime.Unknown;
switch (version[1]) {
case '1':
if (version.Length <= 3)
return TargetRuntime.Unknown;
if (version[3] == 1)
return TargetRuntime.Net_1_0;
else

30
ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs

@ -92,16 +92,19 @@ namespace ICSharpCode.Decompiler.Metadata @@ -92,16 +92,19 @@ namespace ICSharpCode.Decompiler.Metadata
public UniversalAssemblyResolver(string mainAssemblyFileName, bool throwOnError, string targetFramework,
PEStreamOptions streamOptions = PEStreamOptions.Default, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default)
{
this.mainAssemblyFileName = mainAssemblyFileName;
this.throwOnError = throwOnError;
this.streamOptions = streamOptions;
this.metadataOptions = metadataOptions;
this.targetFramework = targetFramework ?? string.Empty;
(targetFrameworkIdentifier, targetFrameworkVersion) = ParseTargetFramework(this.targetFramework);
this.mainAssemblyFileName = mainAssemblyFileName;
this.baseDirectory = Path.GetDirectoryName(mainAssemblyFileName);
this.throwOnError = throwOnError;
if (string.IsNullOrWhiteSpace(this.baseDirectory))
this.baseDirectory = Environment.CurrentDirectory;
AddSearchDirectory(baseDirectory);
if (mainAssemblyFileName != null) {
string baseDirectory = Path.GetDirectoryName(mainAssemblyFileName);
if (string.IsNullOrWhiteSpace(this.baseDirectory))
this.baseDirectory = Environment.CurrentDirectory;
AddSearchDirectory(baseDirectory);
}
}
internal static (TargetFrameworkIdentifier, Version) ParseTargetFramework(string targetFramework)
@ -191,7 +194,10 @@ namespace ICSharpCode.Decompiler.Metadata @@ -191,7 +194,10 @@ namespace ICSharpCode.Decompiler.Metadata
if (IsZeroOrAllOnes(targetFrameworkVersion))
goto default;
if (dotNetCorePathFinder == null) {
dotNetCorePathFinder = new DotNetCorePathFinder(mainAssemblyFileName, targetFramework, targetFrameworkIdentifier, targetFrameworkVersion);
if (mainAssemblyFileName == null)
dotNetCorePathFinder = new DotNetCorePathFinder(targetFrameworkIdentifier, targetFrameworkVersion);
else
dotNetCorePathFinder = new DotNetCorePathFinder(mainAssemblyFileName, targetFramework, targetFrameworkIdentifier, targetFrameworkVersion);
foreach (var directory in directories) {
dotNetCorePathFinder.AddSearchDirectory(directory);
}
@ -327,9 +333,13 @@ namespace ICSharpCode.Decompiler.Metadata @@ -327,9 +333,13 @@ namespace ICSharpCode.Decompiler.Metadata
if (assembly != null)
return assembly;
assembly = SearchDirectory(name, framework_dirs);
if (assembly != null)
return assembly;
// when decompiling assemblies that target frameworks prior to 4.0, we can fall back to the 4.0 assemblies in case the target framework is not installed.
// but when looking for Microsoft.Build.Framework, Version=15.0.0.0 we should not use the version 4.0 assembly here so that the LoadedAssembly logic can instead fall back to version 15.1.0.0
if (name.Version <= new Version(4, 0, 0, 0)) {
assembly = SearchDirectory(name, framework_dirs);
if (assembly != null)
return assembly;
}
if (throwOnError)
throw new AssemblyResolutionException(name);

63
ICSharpCode.Decompiler/Output/TextTokenWriter.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.OutputVisitor;
using ICSharpCode.Decompiler.CSharp.Resolver;
@ -26,6 +27,7 @@ using ICSharpCode.Decompiler.CSharp.Syntax; @@ -26,6 +27,7 @@ using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
namespace ICSharpCode.Decompiler
{
@ -39,7 +41,7 @@ namespace ICSharpCode.Decompiler @@ -39,7 +41,7 @@ namespace ICSharpCode.Decompiler
bool inDocumentationComment = false;
bool firstUsingDeclaration;
bool lastUsingDeclaration;
public TextTokenWriter(ITextOutput output, DecompilerSettings settings, IDecompilerTypeSystem typeSystem)
{
if (output == null)
@ -52,13 +54,13 @@ namespace ICSharpCode.Decompiler @@ -52,13 +54,13 @@ namespace ICSharpCode.Decompiler
this.settings = settings;
this.typeSystem = typeSystem;
}
public override void WriteIdentifier(Identifier identifier)
{
if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) {
output.Write('@');
}
var definition = GetCurrentDefinition();
string name = TextWriterTokenWriter.EscapeIdentifier(identifier.Name);
switch (definition) {
@ -69,7 +71,7 @@ namespace ICSharpCode.Decompiler @@ -69,7 +71,7 @@ namespace ICSharpCode.Decompiler
output.WriteReference(m, name, true);
return;
}
var member = GetCurrentMemberReference();
switch (member) {
case IType t:
@ -110,6 +112,7 @@ namespace ICSharpCode.Decompiler @@ -110,6 +112,7 @@ namespace ICSharpCode.Decompiler
if (symbol != null && node.Role == Roles.Type && node.Parent is ObjectCreateExpression) {
symbol = node.Parent.GetSymbol();
}
if (node is IdentifierExpression && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression && symbol is IMember member) {
var declaringType = member.DeclaringType;
if (declaringType != null && declaringType.Kind == TypeKind.Delegate)
@ -123,10 +126,8 @@ namespace ICSharpCode.Decompiler @@ -123,10 +126,8 @@ namespace ICSharpCode.Decompiler
if (symbol == null)
return null;
//if (settings.AutomaticEvents && member is FieldDefinition) {
// var field = (FieldDefinition)member;
// return field.DeclaringType.Events.FirstOrDefault(ev => ev.Name == field.Name) ?? member;
//}
if (symbol is LocalFunctionMethod)
return null;
return symbol;
}
@ -142,14 +143,18 @@ namespace ICSharpCode.Decompiler @@ -142,14 +143,18 @@ namespace ICSharpCode.Decompiler
if (letClauseVariable != null)
return letClauseVariable;
var gotoStatement = node as GotoStatement;
if (gotoStatement != null)
{
if (node is GotoStatement gotoStatement) {
var method = nodeStack.Select(nd => nd.GetSymbol() as IMethod).FirstOrDefault(mr => mr != null);
if (method != null)
return method + gotoStatement.Label;
}
if (node.Role == Roles.TargetExpression && node.Parent is InvocationExpression) {
var symbol = node.Parent.GetSymbol();
if (symbol is LocalFunctionMethod)
return symbol;
}
return null;
}
@ -177,29 +182,29 @@ namespace ICSharpCode.Decompiler @@ -177,29 +182,29 @@ namespace ICSharpCode.Decompiler
return method + label.Label;
}
if (node is LocalFunctionDeclarationStatement) {
var localFunction = node.GetResolveResult() as MemberResolveResult;
if (node is MethodDeclaration && node.Parent is LocalFunctionDeclarationStatement) {
var localFunction = node.Parent.GetResolveResult() as MemberResolveResult;
if (localFunction != null)
return localFunction.Member;
}
return null;
}
ISymbol GetCurrentDefinition()
{
if (nodeStack == null || nodeStack.Count == 0)
return null;
var node = nodeStack.Peek();
if (node is Identifier)
node = node.Parent;
if (IsDefinition(ref node))
return node.GetSymbol();
return null;
}
public override void WriteKeyword(Role role, string keyword)
{
//To make reference for 'this' and 'base' keywords in the ClassName():this() expression
@ -211,7 +216,7 @@ namespace ICSharpCode.Decompiler @@ -211,7 +216,7 @@ namespace ICSharpCode.Decompiler
}
output.Write(keyword);
}
public override void WriteToken(Role role, string token)
{
switch (token) {
@ -253,22 +258,22 @@ namespace ICSharpCode.Decompiler @@ -253,22 +258,22 @@ namespace ICSharpCode.Decompiler
break;
}
}
public override void Space()
{
output.Write(' ');
}
public override void Indent()
{
output.Indent();
}
public override void Unindent()
{
output.Unindent();
}
public override void NewLine()
{
if (!firstUsingDeclaration && lastUsingDeclaration) {
@ -277,7 +282,7 @@ namespace ICSharpCode.Decompiler @@ -277,7 +282,7 @@ namespace ICSharpCode.Decompiler
}
output.WriteLine();
}
public override void WriteComment(CommentType commentType, string content)
{
switch (commentType) {
@ -309,7 +314,7 @@ namespace ICSharpCode.Decompiler @@ -309,7 +314,7 @@ namespace ICSharpCode.Decompiler
break;
}
}
public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument)
{
// pre-processor directive must start on its own line
@ -321,7 +326,7 @@ namespace ICSharpCode.Decompiler @@ -321,7 +326,7 @@ namespace ICSharpCode.Decompiler
}
output.WriteLine();
}
public override void WritePrimitiveValue(object value, LiteralFormat format = LiteralFormat.None)
{
new TextWriterTokenWriter(new TextOutputWriter(output)).WritePrimitiveValue(value, format);
@ -376,7 +381,7 @@ namespace ICSharpCode.Decompiler @@ -376,7 +381,7 @@ namespace ICSharpCode.Decompiler
break;
}
}
public override void StartNode(AstNode node)
{
if (nodeStack.Count == 0) {
@ -390,7 +395,7 @@ namespace ICSharpCode.Decompiler @@ -390,7 +395,7 @@ namespace ICSharpCode.Decompiler
}
nodeStack.Push(node);
}
private bool IsUsingDeclaration(AstNode node)
{
return node is UsingDeclaration || node is UsingAliasDeclaration;
@ -401,10 +406,10 @@ namespace ICSharpCode.Decompiler @@ -401,10 +406,10 @@ namespace ICSharpCode.Decompiler
if (nodeStack.Pop() != node)
throw new InvalidOperationException();
}
public static bool IsDefinition(ref AstNode node)
{
if (node is EntityDeclaration)
if (node is EntityDeclaration && !(node.Parent is LocalFunctionDeclarationStatement))
return true;
if (node is VariableInitializer && node.Parent is FieldDeclaration) {
node = node.Parent;

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 this method may only be called on fresh instances.
/// Used with C# 9 `init;` property setters.
/// </summary>
bool IsInitOnly { 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'

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

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

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

@ -31,11 +31,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -31,11 +31,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{
readonly IMethod baseMethod;
public LocalFunctionMethod(IMethod baseMethod, int numberOfCompilerGeneratedParameters, int numberOfCompilerGeneratedTypeParameters)
public LocalFunctionMethod(IMethod baseMethod, string name, bool isStaticLocalFunction, int numberOfCompilerGeneratedParameters, int numberOfCompilerGeneratedTypeParameters)
{
if (baseMethod == null)
throw new ArgumentNullException(nameof(baseMethod));
this.baseMethod = baseMethod;
this.Name = name;
this.IsStaticLocalFunction = isStaticLocalFunction;
this.NumberOfCompilerGeneratedParameters = numberOfCompilerGeneratedParameters;
this.NumberOfCompilerGeneratedTypeParameters = numberOfCompilerGeneratedTypeParameters;
}
@ -46,7 +48,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -46,7 +48,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return false;
return baseMethod.Equals(other.baseMethod, typeNormalization)
&& NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters
&& NumberOfCompilerGeneratedTypeParameters == other.NumberOfCompilerGeneratedTypeParameters;
&& NumberOfCompilerGeneratedTypeParameters == other.NumberOfCompilerGeneratedTypeParameters
&& IsStaticLocalFunction == other.IsStaticLocalFunction;
}
public override bool Equals(object obj)
@ -55,7 +58,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -55,7 +58,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return false;
return baseMethod.Equals(other.baseMethod)
&& NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters
&& NumberOfCompilerGeneratedTypeParameters == other.NumberOfCompilerGeneratedTypeParameters;
&& NumberOfCompilerGeneratedTypeParameters == other.NumberOfCompilerGeneratedTypeParameters
&& IsStaticLocalFunction == other.IsStaticLocalFunction;
}
public override int GetHashCode()
@ -65,14 +69,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -65,14 +69,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public override string ToString()
{
return string.Format("[LocalFunctionMethod: ReducedFrom={0}, NumberOfGeneratedParameters={1}, NumberOfCompilerGeneratedTypeParameters={2}]", ReducedFrom, NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters);
return string.Format("[LocalFunctionMethod: ReducedFrom={0}, Name={1}, NumberOfGeneratedParameters={2}, NumberOfCompilerGeneratedTypeParameters={3}, IsStaticLocalFunction={4}]", ReducedFrom, Name, NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters, IsStaticLocalFunction);
}
internal int NumberOfCompilerGeneratedParameters { get; }
internal int NumberOfCompilerGeneratedTypeParameters { get; }
internal bool IsStaticLocalFunction => NumberOfCompilerGeneratedParameters == 0 && (baseMethod.IsStatic || (baseMethod.DeclaringTypeDefinition.IsCompilerGenerated() && !baseMethod.DeclaringType.GetFields(f => !f.IsStatic).Any()));
internal bool IsStaticLocalFunction { get; }
public IMember MemberDefinition => this;
@ -88,7 +92,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -88,7 +92,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{
return new LocalFunctionMethod(
baseMethod.Specialize(substitution),
NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters);
Name, IsStaticLocalFunction, NumberOfCompilerGeneratedParameters, NumberOfCompilerGeneratedTypeParameters);
}
IMember IMember.Specialize(TypeParameterSubstitution substitution)
@ -96,7 +100,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -96,7 +100,6 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return Specialize(substitution);
}
public IReadOnlyList<ITypeParameter> TypeParameters => baseMethod.TypeParameters;
public bool IsExtensionMethod => baseMethod.IsExtensionMethod;
public bool IsLocalFunction => true;
public bool IsConstructor => baseMethod.IsConstructor;
@ -107,7 +110,24 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -107,7 +110,24 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public IMember AccessorOwner => baseMethod.AccessorOwner;
public MethodSemanticsAttributes AccessorKind => baseMethod.AccessorKind;
public IMethod ReducedFrom => baseMethod;
public IReadOnlyList<IType> TypeArguments => baseMethod.TypeArguments;
List<ITypeParameter> typeParameters;
public IReadOnlyList<ITypeParameter> TypeParameters {
get {
if (typeParameters == null)
typeParameters = new List<ITypeParameter>(baseMethod.TypeParameters.Skip(NumberOfCompilerGeneratedTypeParameters));
return typeParameters;
}
}
List<IType> typeArguments;
public IReadOnlyList<IType> TypeArguments {
get {
if (typeArguments == null)
typeArguments = new List<IType>(baseMethod.TypeArguments.Skip(NumberOfCompilerGeneratedTypeParameters));
return typeArguments;
}
}
List<IParameter> parameters;
public IReadOnlyList<IParameter> Parameters {
@ -127,6 +147,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -127,6 +147,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes();
bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly;
bool IMethod.ThisIsRefReadOnly => baseMethod.ThisIsRefReadOnly;
bool IMethod.IsInitOnly => baseMethod.IsInitOnly;
/// <summary>
/// We consider local functions as always static, because they do not have a "this parameter".
/// Even local functions in instance methods capture this.
@ -137,8 +158,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -137,8 +158,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public Accessibility Accessibility => baseMethod.Accessibility;
public string FullName => baseMethod.FullName;
public string Name => baseMethod.Name;
public string FullName => Name;
public string Name { get; set; }
public string ReflectionName => baseMethod.ReflectionName;
public string Namespace => baseMethod.Namespace;

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

@ -50,6 +50,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -50,6 +50,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IType returnType;
byte returnTypeIsRefReadonly = ThreeState.Unknown;
byte thisIsRefReadonly = ThreeState.Unknown;
bool isInitOnly;
internal MetadataMethod(MetadataModule module, MethodDefinitionHandle handle)
{
@ -150,6 +151,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -150,6 +151,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
}
public bool IsInitOnly {
get {
var returnType = LazyInit.VolatileRead(ref this.returnType);
if (returnType == null)
DecodeSignature();
return this.isInitOnly;
}
}
internal Nullability NullableContext {
get {
var methodDef = module.metadata.GetMethodDefinition(handle);
@ -163,19 +173,23 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -163,19 +173,23 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
var genericContext = new GenericContext(DeclaringType.TypeParameters, this.TypeParameters);
IType returnType;
IParameter[] parameters;
ModifiedType mod;
try {
var nullableContext = methodDef.GetCustomAttributes().GetNullableContext(module.metadata) ?? DeclaringTypeDefinition.NullableContext;
var signature = methodDef.DecodeSignature(module.TypeProvider, genericContext);
(returnType, parameters) = DecodeSignature(module, this, signature, methodDef.GetParameters(), nullableContext, module.OptionsForEntity(this));
(returnType, parameters, mod) = DecodeSignature(module, this, signature, methodDef.GetParameters(), nullableContext, module.OptionsForEntity(this));
} catch (BadImageFormatException) {
returnType = SpecialType.UnknownType;
parameters = Empty<IParameter>.Array;
mod = null;
}
this.isInitOnly = mod is { Modifier: { Name: "IsExternalInit", Namespace: "System.Runtime.CompilerServices" } };
LazyInit.GetOrSet(ref this.returnType, returnType);
LazyInit.GetOrSet(ref this.parameters, parameters);
}
internal static (IType, IParameter[]) DecodeSignature(MetadataModule module, IParameterizedMember owner,
internal static (IType returnType, IParameter[] parameters, ModifiedType returnTypeModifier) DecodeSignature(
MetadataModule module, IParameterizedMember owner,
MethodSignature<IType> signature, ParameterHandleCollection? parameterHandles,
Nullability nullableContext, TypeSystemOptions typeSystemOptions,
CustomAttributeHandleCollection? returnTypeAttributes = null)
@ -231,7 +245,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -231,7 +245,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
Debug.Assert(i == parameters.Length);
var returnType = ApplyAttributeTypeVisitor.ApplyAttributesToType(signature.ReturnType,
module.Compilation, returnTypeAttributes, metadata, typeSystemOptions, nullableContext);
return (returnType, parameters);
return (returnType, parameters, signature.ReturnType as ModifiedType);
}
#endregion
@ -453,7 +467,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -453,7 +467,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
#endregion
public Accessibility Accessibility => GetAccessibility(attributes);
internal static Accessibility GetAccessibility(MethodAttributes attr)
{
switch (attr & MethodAttributes.MemberAccessMask) {

3
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs

@ -153,7 +153,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -153,7 +153,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
// Roslyn uses the same workaround (see the NullableTypeDecoder.TransformType
// call in PEPropertySymbol).
var typeOptions = module.OptionsForEntity(declTypeDef);
(returnType, parameters) = MetadataMethod.DecodeSignature(module, this, signature,
(returnType, parameters, _) = MetadataMethod.DecodeSignature(
module, this, signature,
parameterHandles, nullableContext, typeOptions,
returnTypeAttributes: propertyDef.GetCustomAttributes());
} catch (BadImageFormatException) {

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

@ -98,6 +98,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -98,6 +98,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public bool ReturnTypeIsRefReadOnly => methodDefinition.ReturnTypeIsRefReadOnly;
bool IMethod.ThisIsRefReadOnly => methodDefinition.ThisIsRefReadOnly;
bool IMethod.IsInitOnly => methodDefinition.IsInitOnly;
public IReadOnlyList<ITypeParameter> TypeParameters {
get {

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

@ -62,6 +62,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -62,6 +62,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
bool IMethod.ReturnTypeIsRefReadOnly => underlyingMethod.ReturnTypeIsRefReadOnly;
bool IMethod.ThisIsRefReadOnly => underlyingMethod.ThisIsRefReadOnly;
bool IMethod.IsInitOnly => underlyingMethod.IsInitOnly;
IReadOnlyList<ITypeParameter> IMethod.TypeParameters => EmptyList<ITypeParameter>.Instance;
IReadOnlyList<IType> IMethod.TypeArguments => EmptyList<IType>.Instance;

1
ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs

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

8
ICSharpCode.Decompiler/Util/CollectionExtensions.cs

@ -12,10 +12,12 @@ namespace ICSharpCode.Decompiler.Util @@ -12,10 +12,12 @@ namespace ICSharpCode.Decompiler.Util
value = pair.Value;
}
#if !NETCORE
public static IEnumerable<(A, B)> Zip<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2)
{
return input1.Zip(input2, (a, b) => (a, b));
}
#endif
public static IEnumerable<(A, B)> ZipLongest<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2)
{
@ -51,10 +53,12 @@ namespace ICSharpCode.Decompiler.Util @@ -51,10 +53,12 @@ namespace ICSharpCode.Decompiler.Util
}
}
#if !NETCORE
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> input)
{
return new HashSet<T>(input);
}
#endif
public static IEnumerable<T> SkipLast<T>(this IReadOnlyCollection<T> input, int count)
{
@ -323,7 +327,7 @@ namespace ICSharpCode.Decompiler.Util @@ -323,7 +327,7 @@ namespace ICSharpCode.Decompiler.Util
return first;
}
#region Aliases/shortcuts for Enumerable extension methods
#region Aliases/shortcuts for Enumerable extension methods
public static bool Any<T>(this ICollection<T> list) => list.Count > 0;
public static bool Any<T>(this T[] array, Predicate<T> match) => Array.Exists(array, match);
public static bool Any<T>(this List<T> list, Predicate<T> match) => list.Exists(match);
@ -335,6 +339,6 @@ namespace ICSharpCode.Decompiler.Util @@ -335,6 +339,6 @@ namespace ICSharpCode.Decompiler.Util
public static T FirstOrDefault<T>(this List<T> list, Predicate<T> predicate) => list.Find(predicate);
public static T Last<T>(this IList<T> list) => list[list.Count - 1];
#endregion
#endregion
}
}

23
ILSpy.AddIn/AssemblyFileFinder.cs

@ -1,32 +1,29 @@ @@ -1,32 +1,29 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Util;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.AddIn
{
public class AssemblyFileFinder
{
public static string FindAssemblyFile(AssemblyDefinition assemblyDefinition, string assemblyFile)
public static string FindAssemblyFile(Mono.Cecil.AssemblyDefinition assemblyDefinition, string assemblyFile)
{
var assemblyResolver = new UniversalAssemblyResolver(assemblyFile, false,
DetectTargetFrameworkId(assemblyDefinition, assemblyFile));
string tfi = DetectTargetFrameworkId(assemblyDefinition, assemblyFile);
UniversalAssemblyResolver assemblyResolver;
if (IsReferenceAssembly(assemblyDefinition, assemblyFile)) {
assemblyResolver.RemoveSearchDirectory(Path.GetDirectoryName(assemblyFile));
assemblyResolver = new UniversalAssemblyResolver(null, throwOnError: false, tfi);
} else {
assemblyResolver = new UniversalAssemblyResolver(assemblyFile, throwOnError: false, tfi);
}
return assemblyResolver.FindAssemblyFile(
ICSharpCode.Decompiler.Metadata.AssemblyNameReference.Parse(assemblyDefinition.Name.FullName));
return assemblyResolver.FindAssemblyFile(AssemblyNameReference.Parse(assemblyDefinition.Name.FullName));
}
static readonly string RefPathPattern = @"NuGetFallbackFolder[/\\][^/\\]+[/\\][^/\\]+[/\\]ref[/\\]";
public static bool IsReferenceAssembly(AssemblyDefinition assemblyDef, string assemblyFile)
public static bool IsReferenceAssembly(Mono.Cecil.AssemblyDefinition assemblyDef, string assemblyFile)
{
if (assemblyDef.CustomAttributes.Any(ca => ca.AttributeType.FullName == "System.Runtime.CompilerServices.ReferenceAssemblyAttribute"))
return true;
@ -40,7 +37,7 @@ namespace ICSharpCode.ILSpy.AddIn @@ -40,7 +37,7 @@ namespace ICSharpCode.ILSpy.AddIn
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<1>.NETFramework)[/\\]v(?<2>[^/\\]+)[/\\])" +
@"|((NuGetFallbackFolder|packs)[/\\](?<1>[^/\\]+)\\(?<2>[^/\\]+)([/\\].*)?[/\\]ref[/\\])";
public static string DetectTargetFrameworkId(AssemblyDefinition assembly, string assemblyPath = null)
public static string DetectTargetFrameworkId(Mono.Cecil.AssemblyDefinition assembly, string assemblyPath = null)
{
if (assembly == null)
throw new ArgumentNullException(nameof(assembly));

14
ILSpy.AddIn/Commands/OpenCodeItemCommand.cs

@ -38,10 +38,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -38,10 +38,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
var document = owner.DTE.ActiveDocument;
menuItem.Visible =
(document != null) &&
(document.ProjectItem != null) &&
(document.ProjectItem.ContainingProject != null) &&
(document.ProjectItem.ContainingProject.ConfigurationManager != null) &&
(document?.ProjectItem?.ContainingProject?.ConfigurationManager != null) &&
!string.IsNullOrEmpty(document.ProjectItem.ContainingProject.FileName);
}
}
@ -51,7 +48,6 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -51,7 +48,6 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
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();
if (id == null)
return null;
@ -59,14 +55,6 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -59,14 +55,6 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
return owner.Workspace.CurrentSolution.GetDocument(id);
}
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();

5
ILSpy.AddIn/Commands/OpenILSpyCommand.cs

@ -50,7 +50,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -50,7 +50,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
protected string GetILSpyPath()
{
var basePath = Path.GetDirectoryName(typeof(ILSpyAddInPackage).Assembly.Location);
return Path.Combine(basePath, "ILSpy.exe");
return Path.Combine(basePath, "ILSpy", "ILSpy.exe");
}
protected void OpenAssembliesInILSpy(ILSpyParameters parameters)
@ -80,8 +80,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -80,8 +80,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
using (var assemblyDef = AssemblyDefinition.ReadAssembly(reference.Display)) {
string assemblyName = assemblyDef.Name.Name;
string resolvedAssemblyFile = AssemblyFileFinder.FindAssemblyFile(assemblyDef, reference.Display);
dict.Add(assemblyName,
new DetectedReference(assemblyName, resolvedAssemblyFile, false));
dict.Add(assemblyName, new DetectedReference(assemblyName, resolvedAssemblyFile, false));
}
}
foreach (var projectReference in parentProject.ProjectReferences) {

39
ILSpy.AddIn/ILSpy.AddIn.csproj

@ -51,10 +51,15 @@ @@ -51,10 +51,15 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ILSpy\ILSpy.csproj" />
<ProjectReference Include="..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj" />
<ProjectReference Include="..\ILSpy\ILSpy.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<Private>false</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\ILSpy\NativeMethods.cs" Link="NativeMethods.cs" />
<Compile Include="Commands\AssemblyReferenceForILSpy.cs" />
<Compile Include="Commands\NuGetReferenceForILSpy.cs" />
<Compile Include="Commands\ProjectItemForILSpy.cs" />
@ -102,37 +107,19 @@ @@ -102,37 +107,19 @@
</PropertyGroup>
<ItemGroup>
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Collections.Immutable.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Composition.AttributedModel.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Composition.Convention.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Composition.Hosting.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Composition.Runtime.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Composition.TypedParts.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Threading.Tasks.Dataflow.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)ICSharpCode.AvalonEdit.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Humanizer.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)System.Reflection.Metadata.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Microsoft.VisualStudio.Composition.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Microsoft.VisualStudio.Validation.dll" />
<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)OSVersionHelper.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Xceed.Wpf.AvalonDock.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)DataGridExtensions.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)Iced.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)ILCompiler.Reflection.ReadyToRun.dll" />
<AdditionalDependencies Include="$(ILSpyBuildPath)ILSpy.ReadyToRun.Plugin.dll" />
<Content Include="$(OutputPath)Mono.Cecil.dll">
<IncludeInVSIX>true</IncludeInVSIX>
<VSIXSubPath>\</VSIXSubPath>
</Content>
</ItemGroup>
<!-- END WORKAROUND -->
<ItemGroup>
<Content Include="@(AdditionalDependencies)">
<Content Include="$(ILSpyBuildPath)*.dll;$(ILSpyBuildPath)ILSpy.exe;$(ILSpyBuildPath)ILSpy.exe.config">
<IncludeInVSIX>true</IncludeInVSIX>
<VSIXSubPath>\</VSIXSubPath>
<VSIXSubPath>\ILSpy</VSIXSubPath>
</Content>
</ItemGroup>
<!-- END WORKAROUND -->
<ItemGroup>
<None Include="ILSpyAddIn.vsct" />

6
ILSpy.AddIn/ILSpyInstance.cs

@ -12,11 +12,11 @@ namespace ICSharpCode.ILSpy.AddIn @@ -12,11 +12,11 @@ namespace ICSharpCode.ILSpy.AddIn
{
public ILSpyParameters(IEnumerable<string> assemblyFileNames, params string[] arguments)
{
this.AssemblyFileNames = assemblyFileNames;
this.AssemblyFileNames = assemblyFileNames.ToArray();
this.Arguments = arguments;
}
public IEnumerable<string> AssemblyFileNames { get; private set; }
public string[] AssemblyFileNames { get; private set; }
public string[] Arguments { get; private set; }
}
@ -32,7 +32,7 @@ namespace ICSharpCode.ILSpy.AddIn @@ -32,7 +32,7 @@ namespace ICSharpCode.ILSpy.AddIn
static string GetILSpyPath()
{
var basePath = Path.GetDirectoryName(typeof(ILSpyAddInPackage).Assembly.Location);
return Path.Combine(basePath, "ILSpy.exe");
return Path.Combine(basePath, "ILSpy", "ILSpy.exe");
}
public void Start()

8
ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj

@ -1,6 +1,5 @@ @@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.props" />
<Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
@ -10,6 +9,9 @@ @@ -10,6 +9,9 @@
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
<EnableDefaultItems>false</EnableDefaultItems>
<UseWpf>true</UseWpf>
<ExtrasEnableDefaultPageItems>false</ExtrasEnableDefaultPageItems>
<ExtrasEnableDefaultResourceItems>false</ExtrasEnableDefaultResourceItems>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
@ -75,8 +77,6 @@ @@ -75,8 +77,6 @@
<PackageReference Include="ILCompiler.Reflection.ReadyToRun" Version="1.0.8-alpha" />
</ItemGroup>
<Import Sdk="Microsoft.NET.Sdk" Project="Sdk.targets" />
<Target Name="RemoveTransitiveProjectReferences" AfterTargets="IncludeTransitiveProjectReferences">
<ItemGroup>
<ProjectReference Remove="@(_TransitiveProjectReferences)" />

15
ILSpy/App.xaml.cs

@ -165,6 +165,9 @@ namespace ICSharpCode.ILSpy @@ -165,6 +165,9 @@ namespace ICSharpCode.ILSpy
}
}
[ThreadStatic]
static bool showingError;
static void UnhandledException(Exception exception)
{
Debug.WriteLine(exception.ToString());
@ -176,7 +179,17 @@ namespace ICSharpCode.ILSpy @@ -176,7 +179,17 @@ namespace ICSharpCode.ILSpy
break;
}
}
MessageBox.Show(exception.ToString(), "Sorry, we crashed");
if (showingError) {
// Ignore re-entrant calls
// We run the risk of opening an infinite number of exception dialogs.
return;
}
showingError = true;
try {
MessageBox.Show(exception.ToString(), "Sorry, we crashed");
} finally {
showingError = false;
}
}
#endregion

44
ILSpy/Commands/DecompileAllCommand.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
#if DEBUG
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
@ -40,29 +41,32 @@ namespace ICSharpCode.ILSpy @@ -40,29 +41,32 @@ namespace ICSharpCode.ILSpy
{
Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput();
Parallel.ForEach(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, delegate(LoadedAssembly asm) {
if (!asm.HasLoadError) {
Stopwatch w = Stopwatch.StartNew();
Exception exception = null;
using (var writer = new System.IO.StreamWriter("c:\\temp\\decompiled\\" + asm.ShortName + ".cs")) {
try {
new CSharpLanguage().DecompileAssembly(asm, new Decompiler.PlainTextOutput(writer), new DecompilationOptions() { FullDecompilation = true, CancellationToken = ct });
Parallel.ForEach(
Partitioner.Create( MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), loadBalance: true),
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct },
delegate(LoadedAssembly asm) {
if (!asm.HasLoadError) {
Stopwatch w = Stopwatch.StartNew();
Exception exception = null;
using (var writer = new System.IO.StreamWriter("c:\\temp\\decompiled\\" + asm.ShortName + ".cs")) {
try {
new CSharpLanguage().DecompileAssembly(asm, new Decompiler.PlainTextOutput(writer), new DecompilationOptions() { FullDecompilation = true, CancellationToken = ct });
}
catch (Exception ex) {
writer.WriteLine(ex.ToString());
exception = ex;
}
}
catch (Exception ex) {
writer.WriteLine(ex.ToString());
exception = ex;
lock (output) {
output.Write(asm.ShortName + " - " + w.Elapsed);
if (exception != null) {
output.Write(" - ");
output.Write(exception.GetType().Name);
}
output.WriteLine();
}
}
lock (output) {
output.Write(asm.ShortName + " - " + w.Elapsed);
if (exception != null) {
output.Write(" - ");
output.Write(exception.GetType().Name);
}
output.WriteLine();
}
}
});
});
return output;
}, ct)).Then(output => Docking.DockWorkspace.Instance.ShowText(output)).HandleExceptions();
}

9
ILSpy/Commands/DisassembleAllCommand.cs

@ -23,6 +23,8 @@ using System.Diagnostics; @@ -23,6 +23,8 @@ using System.Diagnostics;
using System.Threading.Tasks;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.Properties;
using System.Collections.Concurrent;
namespace ICSharpCode.ILSpy
{
[ExportMainMenuCommand(Menu = nameof(Resources._File), Header = nameof(Resources.DEBUGDisassemble), MenuCategory = nameof(Resources.Open), MenuOrder = 2.5)]
@ -37,8 +39,11 @@ namespace ICSharpCode.ILSpy @@ -37,8 +39,11 @@ namespace ICSharpCode.ILSpy
{
Docking.DockWorkspace.Instance.RunWithCancellation(ct => Task<AvalonEditTextOutput>.Factory.StartNew(() => {
AvalonEditTextOutput output = new AvalonEditTextOutput();
Parallel.ForEach(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct }, delegate(LoadedAssembly asm) {
if (!asm.HasLoadError) {
Parallel.ForEach(
Partitioner.Create(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), loadBalance: true),
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct },
delegate (LoadedAssembly asm) {
if (!asm.HasLoadError) {
Stopwatch w = Stopwatch.StartNew();
Exception exception = null;
using (var writer = new System.IO.StreamWriter("c:\\temp\\disassembled\\" + asm.Text.Replace("(", "").Replace(")", "").Replace(' ', '_') + ".il")) {

4
ILSpy/ILSpy.csproj

@ -128,9 +128,7 @@ @@ -128,9 +128,7 @@
<Compile Include="Commands\ShowDebugSteps.cs" />
<Compile Include="Commands\SortAssemblyListCommand.cs" />
<Compile Include="Controls\BoolToVisibilityConverter.cs" />
<Compile Include="Controls\CustomDialog.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Controls\CustomDialog.cs" />
<Compile Include="Controls\GridViewColumnAutoSize.cs" />
<Compile Include="Controls\MarkupExtensions.cs" />
<Compile Include="Controls\ResourceObjectTable.xaml.cs">

2
ILSpy/Languages/CSharpHighlightingTokenWriter.cs

@ -184,8 +184,10 @@ namespace ICSharpCode.ILSpy @@ -184,8 +184,10 @@ namespace ICSharpCode.ILSpy
case "set":
case "add":
case "remove":
case "init":
if (role == PropertyDeclaration.GetKeywordRole ||
role == PropertyDeclaration.SetKeywordRole ||
role == PropertyDeclaration.InitKeywordRole ||
role == CustomEventDeclaration.AddKeywordRole ||
role == CustomEventDeclaration.RemoveKeywordRole)
color = accessorKeywordsColor;

3
ILSpy/Languages/CSharpLanguage.cs

@ -648,6 +648,9 @@ namespace ICSharpCode.ILSpy @@ -648,6 +648,9 @@ namespace ICSharpCode.ILSpy
if (!settings.LiftNullables) {
flags &= ~ConversionFlags.UseNullableSpecifierForValueTypes;
}
if (entity is IMethod m && m.IsLocalFunction) {
writer.WriteIdentifier(Identifier.Create("(local)"));
}
new CSharpAmbience() {
ConversionFlags = flags,
}.ConvertSymbol(entity, writer, settings.CSharpFormattingOptions);

42
ILSpy/LoadedAssembly.cs

@ -20,17 +20,21 @@ using System; @@ -20,17 +20,21 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.PdbProvider;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.Options;
namespace ICSharpCode.ILSpy
@ -318,6 +322,17 @@ namespace ICSharpCode.ILSpy @@ -318,6 +322,17 @@ namespace ICSharpCode.ILSpy
static readonly Dictionary<string, LoadedAssembly> loadingAssemblies = new Dictionary<string, LoadedAssembly>();
MyUniversalResolver universalResolver;
/// <summary>
/// 1) try to find exact match by tfm + full asm name in loaded assemblies
/// 2) try to find match in search paths
/// 3) if a.deps.json is found: search %USERPROFILE%/.nuget/packages/* as well
/// 4) look in /dotnet/shared/{runtime-pack}/{closest-version}
/// 5) if the version is retargetable or all zeros or ones, search C:\Windows\Microsoft.NET\Framework64\v4.0.30319
/// 6) For "mscorlib.dll" we use the exact same assembly with which ILSpy runs
/// 7) Search the GAC
/// 8) search C:\Windows\Microsoft.NET\Framework64\v4.0.30319
/// 9) try to find match by asm name (no tfm/version) in loaded assemblies
/// </summary>
LoadedAssembly LookupReferencedAssemblyInternal(IAssemblyReference fullName, bool isWinRT, string tfm)
{
string key = tfm + ";" + (isWinRT ? fullName.Name : fullName.FullName);
@ -359,8 +374,29 @@ namespace ICSharpCode.ILSpy @@ -359,8 +374,29 @@ namespace ICSharpCode.ILSpy
LoadedAssemblyReferencesInfo.AddMessage(fullName.ToString(), MessageKind.Info, "Success - Loading from: " + file);
asm = new LoadedAssembly(assemblyList, file) { IsAutoLoaded = true };
} else {
LoadedAssemblyReferencesInfo.AddMessageOnce(fullName.ToString(), MessageKind.Error, "Could not find reference: " + fullName);
return null;
var candidates = new List<(LoadedAssembly assembly, Version version)>();
foreach (LoadedAssembly loaded in assemblyList.GetAssemblies()) {
var module = loaded.GetPEFileOrNull();
var reader = module?.Metadata;
if (reader == null || !reader.IsAssembly) continue;
var asmDef = reader.GetAssemblyDefinition();
var asmDefName = reader.GetString(asmDef.Name);
if (fullName.Name.Equals(asmDefName, StringComparison.OrdinalIgnoreCase)) {
candidates.Add((loaded, asmDef.Version));
}
}
if (candidates.Count == 0) {
LoadedAssemblyReferencesInfo.AddMessageOnce(fullName.ToString(), MessageKind.Error, "Could not find reference: " + fullName);
return null;
}
candidates.SortBy(c => c.version);
var bestCandidate = candidates.FirstOrDefault(c => c.version >= fullName.Version).assembly ?? candidates.Last().assembly;
LoadedAssemblyReferencesInfo.AddMessageOnce(fullName.ToString(), MessageKind.Info, "Success - Found in Assembly List with different TFM or version: " + bestCandidate.fileName);
return bestCandidate;
}
loadingAssemblies.Add(file, asm);
}
@ -371,7 +407,7 @@ namespace ICSharpCode.ILSpy @@ -371,7 +407,7 @@ namespace ICSharpCode.ILSpy
lock (loadingAssemblies) {
loadingAssemblies.Remove(file);
}
});
}, DispatcherPriority.Normal);
return asm;
}

9
ILSpy/MainWindow.xaml.cs

@ -408,6 +408,9 @@ namespace ICSharpCode.ILSpy @@ -408,6 +408,9 @@ namespace ICSharpCode.ILSpy
found = true;
} else {
IEntity mr = await Task.Run(() => FindEntityInRelevantAssemblies(navigateTo, relevantAssemblies));
// Make sure we wait for assemblies being loaded...
// BeginInvoke in LoadedAssembly.LookupReferencedAssemblyInternal
await Dispatcher.InvokeAsync(delegate { }, DispatcherPriority.Normal);
if (mr != null && mr.ParentModule.PEFile != null) {
found = true;
if (AssemblyTreeView.SelectedItem == initialSelection) {
@ -483,6 +486,12 @@ namespace ICSharpCode.ILSpy @@ -483,6 +486,12 @@ namespace ICSharpCode.ILSpy
return false;
}
// We intentionally ignore reference assemblies, so that the loop continues looking for another assembly that might have a usable definition.
if (module.IsReferenceAssembly()) {
typeHandle = default;
return false;
}
switch (typeRef) {
case GetPotentiallyNestedClassTypeReference topLevelType:
typeHandle = topLevelType.ResolveInPEFile(module);

1
ILSpy/Metadata/Helpers.cs

@ -113,6 +113,7 @@ namespace ICSharpCode.ILSpy.Metadata @@ -113,6 +113,7 @@ namespace ICSharpCode.ILSpy.Metadata
};
switch (e.PropertyName) {
case "RID":
case "Meaning":
e.Column.SetTemplate((ControlTemplate)MetadataTableViews.Instance["DefaultFilter"]);
((DataGridCustomTextColumn)e.Column).ToolTipBinding = null;
break;

2
ILSpy/Properties/AssemblyInfo.template.cs

@ -37,7 +37,7 @@ using System.Diagnostics.CodeAnalysis; @@ -37,7 +37,7 @@ using System.Diagnostics.CodeAnalysis;
internal static class RevisionClass
{
public const string Major = "6";
public const string Minor = "0";
public const string Minor = "1";
public const string Build = "0";
public const string Revision = "$INSERTREVISION$";
public const string VersionName = null;

7
ILSpy/SolutionWriter.cs

@ -99,7 +99,12 @@ namespace ICSharpCode.ILSpy @@ -99,7 +99,12 @@ namespace ICSharpCode.ILSpy
Stopwatch stopwatch = Stopwatch.StartNew();
try {
await Task.Run(() => Parallel.ForEach(assemblies, n => WriteProject(n, language, solutionDirectory, ct)))
// Explicitly create an enumerable partitioner here to avoid Parallel.ForEach's special cases for lists,
// as those seem to use static partitioning which is inefficient if assemblies take differently
// long to decompile.
await Task.Run(() => Parallel.ForEach(Partitioner.Create(assemblies),
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = ct },
n => WriteProject(n, language, solutionDirectory, ct)))
.ConfigureAwait(false);
await Task.Run(() => SolutionCreator.WriteSolutionFile(solutionFilePath, projects))

Loading…
Cancel
Save