Browse Source

Transform display classes used in local functions.

pull/1586/head
Siegfried Pammer 6 years ago
parent
commit
8d1522f387
  1. 66
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  2. 45
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  3. 9
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs
  4. 37
      ICSharpCode.Decompiler/CSharp/Resolver/LocalFunctionReferenceResolveResult.cs
  5. 11
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  6. 20
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs
  7. 2
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs
  8. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  9. 2
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  10. 14
      ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
  11. 2
      ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
  12. 36
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
  13. 96
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
  14. 10
      ICSharpCode.Decompiler/Output/TextTokenWriter.cs
  15. 4
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  16. 2
      ICSharpCode.Decompiler/TypeSystem/IMethod.cs

66
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -38,6 +38,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -38,6 +38,7 @@ namespace ICSharpCode.Decompiler.CSharp
{
public OpCode CallOpCode;
public bool NeedsBoxingConversion;
public bool IsLocalFunction;
}
struct ArgumentList
@ -52,13 +53,23 @@ namespace ICSharpCode.Decompiler.CSharp @@ -52,13 +53,23 @@ namespace ICSharpCode.Decompiler.CSharp
public bool AddNamesToPrimitiveValues;
public bool IsExpandedForm;
public int LocalFunctionParameterCount;
public int Length => Arguments.Length;
private int GetActualArgumentCount()
{
if (LocalFunctionParameterCount < 0 && FirstOptionalArgumentIndex < 0)
return Arguments.Length;
if (LocalFunctionParameterCount < 0)
return FirstOptionalArgumentIndex;
if (FirstOptionalArgumentIndex < 0)
return LocalFunctionParameterCount;
return Math.Min(FirstOptionalArgumentIndex, LocalFunctionParameterCount);
}
public IEnumerable<ResolveResult> GetArgumentResolveResults(int skipCount = 0)
{
return FirstOptionalArgumentIndex < 0
? Arguments.Skip(skipCount).Select(a => a.ResolveResult)
: Arguments.Skip(skipCount).Take(FirstOptionalArgumentIndex).Select(a => a.ResolveResult);
return Arguments.Skip(skipCount).Take(GetActualArgumentCount()).Select(a => a.ResolveResult);
}
public IEnumerable<Expression> GetArgumentExpressions(int skipCount = 0)
@ -77,22 +88,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -77,22 +88,12 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
}
int argumentCount = GetActualArgumentCount();
if (ArgumentNames == null) {
if (FirstOptionalArgumentIndex < 0)
return Arguments.Skip(skipCount).Select(arg => arg.Expression);
return Arguments.Skip(skipCount).Take(FirstOptionalArgumentIndex).Select(arg => arg.Expression);
return Arguments.Skip(skipCount).Take(argumentCount).Select(arg => arg.Expression);
} else {
Debug.Assert(skipCount == 0);
if (FirstOptionalArgumentIndex < 0) {
return Arguments.Zip(ArgumentNames,
(arg, name) => {
if (name == null)
return arg.Expression;
else
return new NamedArgumentExpression(name, arg);
});
}
return Arguments.Take(FirstOptionalArgumentIndex).Zip(ArgumentNames.Take(FirstOptionalArgumentIndex),
return Arguments.Take(argumentCount).Zip(ArgumentNames.Take(argumentCount),
(arg, name) => {
if (name == null)
return arg.Expression;
@ -179,11 +180,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -179,11 +180,17 @@ namespace ICSharpCode.Decompiler.CSharp
{
// Used for Call, CallVirt and NewObj
var expectedTargetDetails = new ExpectedTargetDetails {
CallOpCode = callOpCode
CallOpCode = callOpCode,
IsLocalFunction = expressionBuilder.IsLocalFunction(method)
};
TranslatedExpression target;
if (callOpCode == OpCode.NewObj) {
target = default(TranslatedExpression); // no target
} else if (expectedTargetDetails.IsLocalFunction) {
var localFunction = expressionBuilder.ResolveLocalFunction(method);
target = new IdentifierExpression(localFunction.Name)
.WithoutILInstruction()
.WithRR(new LocalFunctionReferenceResolveResult(localFunction.Definition));
} else {
target = expressionBuilder.TranslateTarget(
callArguments.FirstOrDefault(),
@ -210,6 +217,22 @@ namespace ICSharpCode.Decompiler.CSharp @@ -210,6 +217,22 @@ namespace ICSharpCode.Decompiler.CSharp
var argumentList = BuildArgumentList(expectedTargetDetails, target.ResolveResult, method,
firstParamIndex, callArguments, argumentToParameterMap);
if (expectedTargetDetails.IsLocalFunction) {
int parameterCount = 0;
foreach (var param in method.Parameters) {
if (param.IsRef && param.Type is ByReferenceType byRef) {
var type = byRef.ElementType.GetDefinition();
if (type != null && type.IsCompilerGenerated())
break;
}
parameterCount++;
}
argumentList.LocalFunctionParameterCount = parameterCount;
return new InvocationExpression(target, argumentList.GetArgumentExpressions())
.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method,
argumentList.GetArgumentResolveResults().ToList(), isExpandedForm: argumentList.IsExpandedForm, isLocalFunctionInvocation: true));
}
if (method is VarArgInstanceMethod) {
argumentList.FirstOptionalArgumentIndex = -1;
@ -566,10 +589,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -566,10 +589,11 @@ namespace ICSharpCode.Decompiler.CSharp
}
private ArgumentList BuildArgumentList(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, int firstParamIndex,
IReadOnlyList<ILInstruction> callArguments, IReadOnlyList<int> argumentToParameterMap)
private ArgumentList BuildArgumentList(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method,
int firstParamIndex, IReadOnlyList<ILInstruction> callArguments, IReadOnlyList<int> argumentToParameterMap)
{
ArgumentList list = new ArgumentList();
list.LocalFunctionParameterCount = -1;
// Translate arguments to the expected parameter types
var arguments = new List<TranslatedExpression>(method.Parameters.Count);
@ -748,7 +772,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -748,7 +772,9 @@ namespace ICSharpCode.Decompiler.CSharp
if (expressionBuilder.HidesVariableWithName(method.Name)) {
requireTarget = true;
} else {
if (method.IsStatic)
if (expectedTargetDetails.IsLocalFunction)
requireTarget = false;
else if (method.IsStatic)
requireTarget = !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) || method.Name == ".cctor";
else if (method.Name == ".ctor")
requireTarget = true; // always use target for base/this-ctor-call, the constructor initializer pattern depends on this

45
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -68,7 +68,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -68,7 +68,7 @@ namespace ICSharpCode.Decompiler.CSharp
class ExpressionBuilder : ILVisitor<TranslationContext, TranslatedExpression>
{
readonly IDecompilerTypeSystem typeSystem;
readonly ITypeResolveContext decompilationContext;
internal readonly ITypeResolveContext decompilationContext;
internal readonly ILFunction currentFunction;
internal readonly ICompilation compilation;
internal readonly CSharpResolver resolver;
@ -189,7 +189,38 @@ namespace ICSharpCode.Decompiler.CSharp @@ -189,7 +189,38 @@ namespace ICSharpCode.Decompiler.CSharp
internal bool HidesVariableWithName(string name)
{
return currentFunction.Ancestors.OfType<ILFunction>().SelectMany(f => f.Variables).Any(v => v.Name == name);
return currentFunction.Ancestors.OfType<ILFunction>().Any(HidesVariableOrNestedFunction);
bool HidesVariableOrNestedFunction(ILFunction function)
{
foreach (var v in function.Variables) {
if (v.Name == name)
return true;
}
foreach (var (key, (functionName, definition)) in function.LocalFunctions) {
if (functionName == name)
return true;
}
return false;
}
}
internal bool IsLocalFunction(IMethod method)
{
return settings.LocalFunctions && IL.Transforms.LocalFunctionDecompiler.IsLocalFunctionMethod(method);
}
internal (string Name, ILFunction Definition) ResolveLocalFunction(IMethod method)
{
Debug.Assert(IsLocalFunction(method));
foreach (var parent in currentFunction.Ancestors.OfType<ILFunction>()) {
if (parent.LocalFunctions.TryGetValue(method, out var info)) {
return info;
}
}
return (null, null);
}
bool RequiresQualifier(IMember member, TranslatedExpression target)
@ -1823,7 +1854,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1823,7 +1854,7 @@ namespace ICSharpCode.Decompiler.CSharp
return SpecialType.UnknownType;
}
IEnumerable<ParameterDeclaration> MakeParameters(IReadOnlyList<IParameter> parameters, ILFunction function)
internal 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;
@ -1832,17 +1863,19 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1832,17 +1863,19 @@ 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))
break;
}
if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType())
pd.Type = null;
ILVariable v;
if (variables.TryGetValue(i, out v))
if (variables.TryGetValue(i, out var v))
pd.AddAnnotation(new ILVariableResolveResult(v, parameters[i].Type));
yield return pd;
i++;
}
}
internal TranslatedExpression TranslateTarget(ILInstruction target, bool nonVirtualInvocation,
bool memberStatic, IType memberDeclaringType)
{

9
ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs

@ -46,7 +46,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -46,7 +46,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
/// Gets whether a params-Array is being used in its expanded form.
/// </summary>
public readonly bool IsExpandedForm;
/// <summary>
/// Gets whether this invocation is calling a local function.
/// </summary>
public readonly bool IsLocalFunctionInvocation;
readonly IReadOnlyList<int> argumentToParameterMap;
/// <summary>
@ -70,6 +75,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -70,6 +75,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
bool isExtensionMethodInvocation = false,
bool isExpandedForm = false,
bool isDelegateInvocation = false,
bool isLocalFunctionInvocation = false,
IReadOnlyList<int> argumentToParameterMap = null,
IList<ResolveResult> initializerStatements = null,
IType returnTypeOverride = null
@ -80,6 +86,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -80,6 +86,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
this.IsExtensionMethodInvocation = isExtensionMethodInvocation;
this.IsExpandedForm = isExpandedForm;
this.IsDelegateInvocation = isDelegateInvocation;
this.IsLocalFunctionInvocation = isLocalFunctionInvocation;
this.argumentToParameterMap = argumentToParameterMap;
}

37
ICSharpCode.Decompiler/CSharp/Resolver/LocalFunctionReferenceResolveResult.cs

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
// Copyright (c) 2019 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Text;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.CSharp.Resolver
{
class LocalFunctionReferenceResolveResult : ResolveResult
{
public readonly ILFunction Function;
public LocalFunctionReferenceResolveResult(ILFunction function)
: base(SpecialType.NoType)
{
this.Function = function;
}
}
}

11
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -28,6 +28,7 @@ using System; @@ -28,6 +28,7 @@ using System;
using System.Threading;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
using ICSharpCode.Decompiler.CSharp.Resolver;
namespace ICSharpCode.Decompiler.CSharp
{
@ -1020,10 +1021,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1020,10 +1021,14 @@ namespace ICSharpCode.Decompiler.CSharp
{
var stmt = new LocalFunctionDeclarationStatement();
var tsab = CSharpDecompiler.CreateAstBuilder(null);
stmt.Name = function.Method.Name;
stmt.Parameters.AddRange(function.Method.Parameters.Select(tsab.ConvertParameter));
var nestedBuilder = new StatementBuilder(typeSystem, exprBuilder.decompilationContext, function, settings, cancellationToken);
var localFunction = exprBuilder.ResolveLocalFunction(function.Method);
Debug.Assert(localFunction.Definition == function);
stmt.Name = localFunction.Name;
stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function));
stmt.ReturnType = tsab.ConvertType(function.Method.ReturnType);
stmt.Body = ConvertAsBlock(function.Body);
stmt.Body = nestedBuilder.ConvertAsBlock(function.Body);
stmt.AddAnnotation(new LocalFunctionReferenceResolveResult(function));
return stmt;
}
}

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

@ -1,6 +1,22 @@ @@ -1,6 +1,22 @@
using System;
// Copyright (c) 2019 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.Collections.Generic;
using System.Text;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
namespace ICSharpCode.Decompiler.CSharp.Syntax

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

@ -154,7 +154,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -154,7 +154,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
var newResolveResult = new CSharpInvocationResolveResult(
irr.TargetResult, irr.Member, irr.Arguments, irr.OverloadResolutionErrors,
isExtensionMethodInvocation: true, irr.IsExpandedForm, irr.IsDelegateInvocation,
irr.GetArgumentToParameterMap(), irr.InitializerStatements);
irr.IsLocalFunctionInvocation, irr.GetArgumentToParameterMap(), irr.InitializerStatements);
invocationExpression.AddAnnotation(newResolveResult);
}
}

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -60,6 +60,7 @@ @@ -60,6 +60,7 @@
<Compile Include="CSharp\CallBuilder.cs" />
<Compile Include="CSharp\CSharpLanguageVersion.cs" />
<Compile Include="CSharp\RequiredNamespaceCollector.cs" />
<Compile Include="CSharp\Resolver\LocalFunctionReferenceResolveResult.cs" />
<Compile Include="CSharp\SequencePointBuilder.cs" />
<Compile Include="CSharp\Syntax\AstNode.cs" />
<Compile Include="CSharp\Syntax\AstNodeCollection.cs" />

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

@ -110,6 +110,8 @@ namespace ICSharpCode.Decompiler.IL @@ -110,6 +110,8 @@ namespace ICSharpCode.Decompiler.IL
}
}
public Dictionary<IMethod, (string Name, ILFunction Declaration)> LocalFunctions { get; } = new Dictionary<IMethod, (string Name, ILFunction Declaration)>();
public readonly IType ReturnType;
public readonly IReadOnlyList<IParameter> Parameters;

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

@ -162,6 +162,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -162,6 +162,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
break;
}
}
foreach (var method in function.LocalFunctions.Keys.ToArray()) {
var info = function.LocalFunctions[method];
if (!LocalFunctionDecompiler.ParseLocalFunctionName(method.Name, out _, out var newName) || !IsValidName(newName))
newName = null;
function.LocalFunctions[method] = (newName, info.Declaration);
}
// Now generate names:
var mapping = new Dictionary<ILVariable, string>(ILVariableEqualityComparer.Instance);
foreach (var inst in function.Descendants.OfType<IInstructionWithVariableOperand>()) {
@ -174,6 +180,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -174,6 +180,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
v.Name = name;
}
}
foreach (var method in function.LocalFunctions.Keys.ToArray()) {
var info = function.LocalFunctions[method];
var newName = info.Name;
if (newName == null) {
newName = GetAlternativeName("f");
}
function.LocalFunctions[method] = (newName, info.Declaration);
}
}
/// <remarks>

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

@ -136,10 +136,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -136,10 +136,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress);
var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.Delegate, context.CancellationToken);
function.DelegateType = value.Method.DeclaringType;
function.CheckInvariant(ILPhase.Normal);
// Embed the lambda into the parent function's ILAst, so that "Show steps" can show
// how the lambda body is being transformed.
value.ReplaceWith(function);
function.CheckInvariant(ILPhase.Normal);
var contextPrefix = targetMethod.Name;
foreach (ILVariable v in function.Variables.Where(v => v.Kind != VariableKind.Parameter)) {

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

@ -1,4 +1,22 @@ @@ -1,4 +1,22 @@
using System;
// Copyright (c) 2019 Siegfried Pammer
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
@ -43,8 +61,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -43,8 +61,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
foreach (var (method, useSites) in localFunctions) {
var insertionPoint = FindInsertionPoint(useSites);
if (TransformLocalFunction(method, (Block)insertionPoint.Parent, insertionPoint.ChildIndex + 1) == null)
ILFunction localFunction = TransformLocalFunction(method, (Block)insertionPoint.Parent, insertionPoint.ChildIndex + 1);
if (localFunction == null)
continue;
function.LocalFunctions.Add(localFunction.Method, (localFunction.Method.Name, localFunction));
}
}
@ -81,7 +101,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -81,7 +101,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return a.Ancestors.FirstOrDefault(ancestorsOfB.Contains);
}
static ILInstruction GetStatement(ILInstruction inst)
internal static bool IsClosureParameter(IParameter parameter)
{
return parameter.IsRef
&& ((ByReferenceType)parameter.Type).ElementType.GetDefinition()?.IsCompilerGenerated() == true;
}
internal static ILInstruction GetStatement(ILInstruction inst)
{
while (inst.Parent != null) {
if (inst.Parent is Block)
@ -161,9 +187,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -161,9 +187,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Newer Roslyn versions use the format "&ltcallerName&gtg__functionName|x_y"
/// Older versions use "&ltcallerName&gtg__functionNamex_y"
/// </summary>
static readonly Regex functionNameRegex = new Regex(@"^<(.*)>g__(.*)\|{0,1}\d+(_\d+)?$", RegexOptions.Compiled);
static readonly Regex functionNameRegex = new Regex(@"^<(.*)>g__([^\|]*)\|{0,1}\d+(_\d+)?$", RegexOptions.Compiled);
static bool ParseLocalFunctionName(string name, out string callerName, out string functionName)
internal static bool ParseLocalFunctionName(string name, out string callerName, out string functionName)
{
callerName = null;
functionName = null;

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

@ -65,20 +65,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -65,20 +65,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
foreach (var v in f.Variables.ToArray()) {
if (HandleMonoStateMachine(function, v, decompilationContext, f))
continue;
if (!(v.IsSingleDefinition && v.StoreInstructions.SingleOrDefault() is StLoc inst))
continue;
if (IsClosureInit(inst, out ITypeDefinition closureType)) {
// TODO : figure out whether it is a mono compiled closure, without relying on the type name
bool isMono = f.StateMachineCompiledWithMono || closureType.Name.Contains("AnonStorey");
displayClasses.Add(inst.Variable, new DisplayClass {
IsMono = isMono,
Initializer = inst,
Variable = v,
Definition = closureType,
Variables = new Dictionary<IField, DisplayClassVariable>(),
CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : inst.Variable.CaptureScope
});
instructionsToRemove.Add(inst);
if (IsClosure(v, out ITypeDefinition closureType, out var inst)) {
AddOrUpdateDisplayClass(f, v, closureType, inst);
}
if (v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index ?? -1] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p)) {
AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body);
}
}
foreach (var displayClass in displayClasses.Values.OrderByDescending(d => d.Initializer.StartILOffset).ToArray()) {
@ -100,6 +91,56 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -100,6 +91,56 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
private void AddOrUpdateDisplayClass(ILFunction f, ILVariable v, ITypeDefinition closureType, ILInstruction inst)
{
var displayClass = displayClasses.Values.FirstOrDefault(c => c.Definition == closureType);
if (displayClass == null) {
// TODO : figure out whether it is a mono compiled closure, without relying on the type name
bool isMono = f.StateMachineCompiledWithMono || closureType.Name.Contains("AnonStorey");
displayClasses.Add(v, new DisplayClass {
IsMono = isMono,
Initializer = inst,
Variable = v,
Definition = closureType,
Variables = new Dictionary<IField, DisplayClassVariable>(),
CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : v.CaptureScope
});
} else {
displayClass.Variable = v;
displayClass.Initializer = inst;
displayClasses.Add(v, displayClass);
}
}
bool IsClosure(ILVariable variable, out ITypeDefinition closureType, out ILInstruction initializer)
{
closureType = null;
initializer = null;
if (variable.IsSingleDefinition && variable.StoreInstructions.SingleOrDefault() is StLoc inst) {
initializer = inst;
if (IsClosureInit(inst, out closureType)) {
instructionsToRemove.Add(inst);
return true;
}
}
closureType = variable.Type.GetDefinition();
if (closureType?.Kind == TypeKind.Struct && variable.HasInitialValue) {
initializer = LocalFunctionDecompiler.GetStatement(variable.AddressInstructions.OrderBy(i => i.StartILOffset).First());
return IsPotentialClosure(this.context, closureType);
}
return false;
}
bool IsClosureInit(StLoc inst, out ITypeDefinition closureType)
{
if (inst.Value is NewObj newObj) {
closureType = newObj.Method.DeclaringTypeDefinition;
return closureType != null && IsPotentialClosure(this.context, newObj);
}
closureType = null;
return false;
}
bool IsOuterClosureReference(IField field)
{
return displayClasses.Values.Any(disp => disp.Definition == field.DeclaringTypeDefinition);
@ -179,6 +220,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -179,6 +220,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return IsPotentialClosure(decompilationContext.CurrentTypeDefinition, inst.Method.DeclaringTypeDefinition);
}
internal static bool IsPotentialClosure(ILTransformContext context, ITypeDefinition potentialDisplayClass)
{
var decompilationContext = new SimpleTypeResolveContext(context.Function.Ancestors.OfType<ILFunction>().Last().Method);
return IsPotentialClosure(decompilationContext.CurrentTypeDefinition, potentialDisplayClass);
}
internal static bool IsPotentialClosure(ITypeDefinition decompiledTypeDefinition, ITypeDefinition potentialDisplayClass)
{
if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
@ -191,6 +238,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -191,6 +238,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
bool IsDisplayClassLoad(ILInstruction target, out ILVariable variable)
{
if (target.MatchLdLoc(out variable) || target.MatchLdLoca(out variable))
return true;
if (target.MatchAddressOf(out var load) && load.MatchLdLoc(out variable))
return true;
return false;
}
protected override void Default(ILInstruction inst)
{
foreach (var child in inst.Children) {
@ -210,13 +266,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -210,13 +266,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
bool IsClosureInit(StLoc inst, out ITypeDefinition closureType)
protected internal override void VisitLdLoc(LdLoc inst)
{
closureType = null;
if (!(inst.Value is NewObj newObj))
return false;
closureType = newObj.Method.DeclaringTypeDefinition;
return closureType != null && IsPotentialClosure(this.context, newObj);
base.VisitLdLoc(inst);
}
protected internal override void VisitStObj(StObj inst)
@ -229,7 +281,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -229,7 +281,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!inst.Target.MatchLdFlda(out ILInstruction target, out IField field))
return;
// Get display class info
if (!(target is LdLoc displayClassLoad && displayClasses.TryGetValue(displayClassLoad.Variable, out var displayClass)))
if (!IsDisplayClassLoad(target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass))
return;
field = (IField)field.MemberDefinition;
if (displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) {
@ -264,7 +316,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -264,7 +316,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!inst.Target.MatchLdFlda(out var target, out IField field))
return;
// Get display class info
if (!(target is LdLoc displayClassLoad && displayClasses.TryGetValue(displayClassLoad.Variable, out var displayClass)))
if (!IsDisplayClassLoad(target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass))
return;
// Get display class variable info
if (!displayClass.Variables.TryGetValue((IField)field.MemberDefinition, out DisplayClassVariable info))

10
ICSharpCode.Decompiler/Output/TextTokenWriter.cs

@ -21,6 +21,7 @@ using System.Collections.Generic; @@ -21,6 +21,7 @@ using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.OutputVisitor;
using ICSharpCode.Decompiler.CSharp.Resolver;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.TypeSystem;
@ -169,13 +170,18 @@ namespace ICSharpCode.Decompiler @@ -169,13 +170,18 @@ namespace ICSharpCode.Decompiler
return variable;
}
var label = node as LabelStatement;
if (label != null) {
if (node is LabelStatement label) {
var method = nodeStack.Select(nd => nd.GetSymbol() as IMethod).FirstOrDefault(mr => mr != null);
if (method != null)
return method + label.Label;
}
if (node is LocalFunctionDeclarationStatement) {
var localFunction = node.GetResolveResult() as LocalFunctionReferenceResolveResult;
if (localFunction != null)
return localFunction.Function.Method;
}
return null;
}

4
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -104,6 +104,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -104,6 +104,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
NullabilityAnnotations = 0x400,
/// <summary>
/// If this option is active,
/// </summary>
LocalFunctions = 0x800,
/// <summary>
/// Default settings: typical options for the decompiler, with all C# languages features enabled.
/// </summary>
Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters

2
ICSharpCode.Decompiler/TypeSystem/IMethod.cs

@ -82,7 +82,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -82,7 +82,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// <summary>
/// If this method is reduced from an extension method return the original method, <c>null</c> otherwise.
/// A reduced method doesn't contain the extension method parameter. That means that has one parameter less than it's definition.
/// A reduced method doesn't contain the extension method parameter. That means that it has one parameter less than its definition.
/// </summary>
IMethod ReducedFrom { get; }

Loading…
Cancel
Save