Browse Source

Merge pull request #1586 from icsharpcode/local-functions

C# 7.0 Local functions
pull/1612/head
Siegfried Pammer 6 years ago committed by GitHub
parent
commit
174d14de3f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
  2. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  3. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  4. 6
      ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs
  5. 76
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/LocalFunctions.cs
  6. 288
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs
  7. 25
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  8. 100
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  9. 42
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  10. 19
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  11. 16
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs
  12. 306
      ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs
  13. 48
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
  14. 21
      ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs
  15. 3
      ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs
  16. 110
      ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs
  17. 2
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  18. 13
      ICSharpCode.Decompiler/DecompilerSettings.cs
  19. 3
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  20. 2
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  21. 4
      ICSharpCode.Decompiler/IL/ILReader.cs
  22. 3
      ICSharpCode.Decompiler/IL/ILVariable.cs
  23. 47
      ICSharpCode.Decompiler/IL/Instructions.cs
  24. 14
      ICSharpCode.Decompiler/IL/Instructions.tt
  25. 163
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  26. 12
      ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
  27. 17
      ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs
  28. 22
      ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs
  29. 1
      ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
  30. 289
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
  31. 2
      ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs
  32. 109
      ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs
  33. 9
      ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs
  34. 11
      ICSharpCode.Decompiler/Output/TextTokenWriter.cs
  35. 6
      ICSharpCode.Decompiler/TypeSystem/IMethod.cs
  36. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs
  37. 138
      ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs
  38. 1
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs
  39. 8
      ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs
  40. 4
      ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs
  41. 2
      ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs
  42. 2
      ILSpy/Languages/ILAstLanguage.cs
  43. 6
      ILSpy/Properties/Resources.Designer.cs
  44. 4
      ILSpy/Properties/Resources.resx
  45. 6
      ILSpy/Properties/Resources.zh-Hans.resx

6
ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs

@ -295,12 +295,6 @@ namespace ICSharpCode.Decompiler.Tests @@ -295,12 +295,6 @@ namespace ICSharpCode.Decompiler.Tests
RunCS(options: options);
}
[Test]
public void LocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions options)
{
RunCS(options: options);
}
void RunCS([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug)
{
string testFileName = testName + ".cs";

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

@ -83,7 +83,7 @@ @@ -83,7 +83,7 @@
<Compile Include="Semantics\ExplicitConversionTest.cs" />
<Compile Include="Semantics\OverloadResolutionTests.cs" />
<Compile Include="DataFlowTest.cs" />
<Compile Include="TestCases\Correctness\LocalFunctions.cs" />
<Compile Include="TestCases\Pretty\LocalFunctions.cs" />
<Compile Include="TestCases\Correctness\RefLocalsAndReturns.cs" />
<Compile Include="TestCases\ILPretty\Issue1256.cs" />
<Compile Include="TestCases\ILPretty\Issue1323.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -184,6 +184,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -184,6 +184,12 @@ namespace ICSharpCode.Decompiler.Tests
});
}
[Test]
public void LocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions);
}
[Test]
public void PropertiesAndEvents([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{

6
ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs

@ -69,11 +69,7 @@ namespace ICSharpCode.Decompiler.Tests @@ -69,11 +69,7 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void ICSharpCode_Decompiler()
{
try {
RunWithTest("ICSharpCode.Decompiler", "ICSharpCode.Decompiler.dll", "ICSharpCode.Decompiler.Tests.exe");
} catch (CompilationFailedException) {
Assert.Ignore("C# 7 local functions not yet supported.");
}
RunWithTest("ICSharpCode.Decompiler", "ICSharpCode.Decompiler.dll", "ICSharpCode.Decompiler.Tests.exe");
}
[Test]

76
ICSharpCode.Decompiler.Tests/TestCases/Correctness/LocalFunctions.cs

@ -1,76 +0,0 @@ @@ -1,76 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LocalFunctions
{
class LocalFunctions
{
int field;
public static void Main(string[] args)
{
StaticContextNoCapture(10);
StaticContextSimpleCapture(10);
StaticContextCaptureInForLoop(10);
var inst = new LocalFunctions() { field = 10 };
inst.ContextNoCapture();
inst.ContextSimpleCapture();
inst.ContextCaptureInForLoop();
}
public static void StaticContextNoCapture(int length)
{
for (int i = 0; i < length; i++) {
LocalWrite("Hello " + i);
}
void LocalWrite(string s) => Console.WriteLine(s);
}
public static void StaticContextSimpleCapture(int length)
{
for (int i = 0; i < length; i++) {
LocalWrite();
}
void LocalWrite() => Console.WriteLine("Hello " + length);
}
public static void StaticContextCaptureInForLoop(int length)
{
for (int i = 0; i < length; i++) {
void LocalWrite() => Console.WriteLine("Hello " + i + "/" + length);
LocalWrite();
}
}
public void ContextNoCapture()
{
for (int i = 0; i < field; i++) {
LocalWrite("Hello " + i);
}
void LocalWrite(string s) => Console.WriteLine(s);
}
public void ContextSimpleCapture()
{
for (int i = 0; i < field; i++) {
LocalWrite();
}
void LocalWrite() => Console.WriteLine("Hello " + field);
}
public void ContextCaptureInForLoop()
{
for (int i = 0; i < field; i++) {
void LocalWrite() => Console.WriteLine("Hello " + i + "/" + field);
LocalWrite();
}
}
}
}

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

@ -0,0 +1,288 @@ @@ -0,0 +1,288 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
namespace LocalFunctions
{
internal class LocalFunctions
{
private int field;
private static void Test(int x)
{
}
private static int GetInt(string a)
{
return a.Length;
}
private static string GetString(int a)
{
return a.ToString();
}
public static void StaticContextNoCapture(int length)
{
for (int i = 0; i < length; i++) {
LocalWrite("Hello " + i);
}
void LocalWrite(string s)
{
Console.WriteLine(s);
}
}
public static void StaticContextSimpleCapture(int length)
{
for (int i = 0; i < length; i++) {
LocalWrite();
}
void LocalWrite()
{
Console.WriteLine("Hello " + length);
}
}
public static void StaticContextCaptureForLoopVariable(int length)
{
int i;
for (i = 0; i < length; i++) {
LocalWrite();
}
void LocalWrite()
{
Console.WriteLine("Hello " + i + "/" + length);
}
}
public void ContextNoCapture()
{
for (int i = 0; i < field; i++) {
LocalWrite("Hello " + i);
}
void LocalWrite(string s)
{
Console.WriteLine(s);
}
}
public void ContextSimpleCapture()
{
for (int i = 0; i < field; i++) {
LocalWrite();
}
void LocalWrite()
{
Console.WriteLine("Hello " + field);
}
}
public void ContextCaptureForLoopVariable()
{
int i;
for (i = 0; i < field; i++) {
LocalWrite();
}
void LocalWrite()
{
Console.WriteLine("Hello " + i + "/" + field);
}
}
public void CapturedOutsideLoop()
{
int i = 0;
while (i < field) {
i = GetInt("asdf");
LocalWrite();
}
void LocalWrite()
{
Console.WriteLine("Hello " + i + "/" + field);
}
}
public void CapturedInForeachLoop(IEnumerable<string> args)
{
foreach (string arg2 in args) {
string arg = arg2;
LocalWrite();
void LocalWrite()
{
Console.WriteLine("Hello " + arg);
}
}
}
public void Overloading()
{
Test(5);
LocalFunctions.Test(2);
void Test(int x)
{
Console.WriteLine("x: {0}", x);
}
}
private void Name()
{
}
private void LocalFunctionHidingMethod()
{
Action action = this.Name;
Name();
action();
void Name()
{
}
}
public void NamedArgument()
{
Use(Get(1), Get(2), Get(3));
Use(Get(1), c: Get(2), b: Get(3));
int Get(int i)
{
return i;
}
void Use(int a, int b, int c)
{
Console.WriteLine(a + b + c);
}
}
public static Func<int> LambdaInLocalFunction()
{
int x = (int)Math.Pow(2.0, 10.0);
return Create();
Func<int> Create()
{
return () => x;
}
}
public static Func<int> MethodRef()
{
int x = (int)Math.Pow(2.0, 10.0);
Enumerable.Range(1, 100).Select(LocalFunction);
return null;
int LocalFunction(int y)
{
return x * y;
}
}
public static int Fib(int i)
{
return FibHelper(i);
int FibHelper(int n)
{
if (n <= 0) {
return 0;
}
return FibHelper(n - 1) + FibHelper(n - 2);
}
}
public int MutuallyRecursiveLocalFunctions()
{
return B(4) + C(3);
int A(int i)
{
if (i > 0) {
return A(i - 1) + 2 * B(i - 1) + 3 * C(i - 1);
}
return 1;
}
int B(int i)
{
if (i > 0) {
return 3 * A(i - 1) + B(i - 1);
}
return 1;
}
int C(int i)
{
if (i > 0) {
return 2 * A(i - 1) + C(i - 1);
}
return 1;
}
}
public static int NestedLocalFunctions(int i)
{
return A();
int A()
{
double x = Math.Pow(10.0, 2.0);
return B();
int B()
{
return i + (int)x;
}
}
}
public static int LocalFunctionInLambda(IEnumerable<int> xs)
{
return xs.First(delegate(int x) {
return Do();
bool Do()
{
return x == 3;
}
});
}
//public static void LocalFunctionInUsing()
//{
// using (MemoryStream memoryStream = new MemoryStream()) {
// Do();
// void Do()
// {
// memoryStream.WriteByte(42);
// }
// }
//}
}
}

25
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -156,6 +156,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -156,6 +156,7 @@ namespace ICSharpCode.Decompiler.CSharp
},
new ProxyCallReplacer(),
new DelegateConstruction(),
new LocalFunctionDecompiler(),
new TransformDisplayClassUsage(),
new HighLevelLoopTransform(),
new ReduceNestingTransform(),
@ -396,7 +397,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -396,7 +397,7 @@ namespace ICSharpCode.Decompiler.CSharp
return new DecompilerTypeSystem(file, resolver);
}
TypeSystemAstBuilder CreateAstBuilder(ITypeResolveContext decompilationContext)
static TypeSystemAstBuilder CreateAstBuilder(ITypeResolveContext decompilationContext)
{
var typeSystemAstBuilder = new TypeSystemAstBuilder();
typeSystemAstBuilder.ShowAttributes = true;
@ -686,6 +687,28 @@ namespace ICSharpCode.Decompiler.CSharp @@ -686,6 +687,28 @@ namespace ICSharpCode.Decompiler.CSharp
connectedMethods.Enqueue((MethodDefinitionHandle)token);
}
break;
case ILOpCode.Call:
case ILOpCode.Callvirt:
// deal with call/callvirt instructions, i.e., local function invocations
token = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32());
if (token.IsNil)
continue;
switch (token.Kind) {
case HandleKind.MethodDefinition:
break;
case HandleKind.MethodSpecification:
var methodSpec = module.Metadata.GetMethodSpecification((MethodSpecificationHandle)token);
if (methodSpec.Method.IsNil || methodSpec.Method.Kind != HandleKind.MethodDefinition)
continue;
token = methodSpec.Method;
break;
default:
continue;
}
if (LocalFunctionDecompiler.IsLocalFunctionMethod(module, (MethodDefinitionHandle)token)) {
connectedMethods.Enqueue((MethodDefinitionHandle)token);
}
break;
default:
blob.SkipOperand(code);
break;

100
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -54,11 +54,16 @@ namespace ICSharpCode.Decompiler.CSharp @@ -54,11 +54,16 @@ namespace ICSharpCode.Decompiler.CSharp
public bool IsExpandedForm;
public int Length => Arguments.Length;
private int GetActualArgumentCount()
{
if (FirstOptionalArgumentIndex < 0)
return Arguments.Length;
return FirstOptionalArgumentIndex;
}
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 +82,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -77,22 +82,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;
@ -181,9 +176,18 @@ namespace ICSharpCode.Decompiler.CSharp @@ -181,9 +176,18 @@ namespace ICSharpCode.Decompiler.CSharp
var expectedTargetDetails = new ExpectedTargetDetails {
CallOpCode = callOpCode
};
ILFunction localFunction = null;
if (method.IsLocalFunction) {
localFunction = expressionBuilder.ResolveLocalFunction(method);
Debug.Assert(localFunction != null);
}
TranslatedExpression target;
if (callOpCode == OpCode.NewObj) {
target = default(TranslatedExpression); // no target
} else if (method.IsLocalFunction && localFunction != null) {
target = new IdentifierExpression(localFunction.Name)
.WithoutILInstruction()
.WithRR(ToMethodGroup(method, localFunction));
} else {
target = expressionBuilder.TranslateTarget(
callArguments.FirstOrDefault(),
@ -210,6 +214,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -210,6 +214,12 @@ namespace ICSharpCode.Decompiler.CSharp
var argumentList = BuildArgumentList(expectedTargetDetails, target.ResolveResult, method,
firstParamIndex, callArguments, argumentToParameterMap);
if (method.IsLocalFunction) {
return new InvocationExpression(target, argumentList.GetArgumentExpressions())
.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method,
argumentList.GetArgumentResolveResults().ToList(), isExpandedForm: argumentList.IsExpandedForm));
}
if (method is VarArgInstanceMethod) {
argumentList.FirstOptionalArgumentIndex = -1;
@ -386,7 +396,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -386,7 +396,7 @@ namespace ICSharpCode.Decompiler.CSharp
argumentList.ArgumentNames = null;
argumentList.AddNamesToPrimitiveValues = false;
var transform = GetRequiredTransformationsForCall(expectedTargetDetails, method, ref unused,
ref argumentList, CallTransformation.None, out IParameterizedMember foundMethod);
ref argumentList, CallTransformation.None, out _);
Debug.Assert(transform == CallTransformation.None || transform == CallTransformation.NoOptionalArgumentAllowed);
// Calls with only one argument do not need an array initializer expression to wrap them.
@ -565,9 +575,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -565,9 +575,8 @@ 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();
@ -748,7 +757,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -748,7 +757,9 @@ namespace ICSharpCode.Decompiler.CSharp
if (expressionBuilder.HidesVariableWithName(method.Name)) {
requireTarget = true;
} else {
if (method.IsStatic)
if (method.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
@ -1110,8 +1121,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1110,8 +1121,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
var op = AssignmentOperatorType.Assign;
var parentEvent = method.AccessorOwner as IEvent;
if (parentEvent != null) {
if (method.AccessorOwner is IEvent parentEvent) {
if (method.Equals(parentEvent.AddAccessor)) {
op = AssignmentOperatorType.Add;
}
@ -1213,7 +1223,19 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1213,7 +1223,19 @@ namespace ICSharpCode.Decompiler.CSharp
TranslatedExpression target;
IType targetType;
bool requireTarget;
if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) {
var expectedTargetDetails = new ExpectedTargetDetails {
CallOpCode = inst.OpCode
};
ResolveResult result = null;
string methodName = method.Name;
if (method.IsLocalFunction) {
requireTarget = false;
var localFunction = expressionBuilder.ResolveLocalFunction(method);
result = ToMethodGroup(method, localFunction);
target = default;
targetType = default;
methodName = localFunction.Name;
} else if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) {
targetType = method.Parameters[0].Type;
if (targetType.Kind == TypeKind.ByReference && thisArg is Box thisArgBox) {
targetType = ((ByReferenceType)targetType).ElementType;
@ -1239,13 +1261,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1239,13 +1261,8 @@ namespace ICSharpCode.Decompiler.CSharp
requireTarget = expressionBuilder.HidesVariableWithName(method.Name)
|| (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression));
}
var expectedTargetDetails = new ExpectedTargetDetails {
CallOpCode = inst.OpCode
};
bool needsCast = false;
ResolveResult result = null;
var or = new OverloadResolution(resolver.Compilation, method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type)));
if (!requireTarget) {
if (!method.IsLocalFunction && !requireTarget) {
result = resolver.ResolveSimpleName(method.Name, method.TypeArguments, isInvocationTarget: false);
if (result is MethodGroupResolveResult mgrr) {
or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray());
@ -1255,9 +1272,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1255,9 +1272,10 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
MemberLookup lookup = null;
bool needsCast = false;
if (requireTarget) {
lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule);
var rr = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false) ;
var rr = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false);
needsCast = true;
result = rr;
if (rr is MethodGroupResolveResult mgrr) {
@ -1272,12 +1290,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1272,12 +1290,12 @@ namespace ICSharpCode.Decompiler.CSharp
}
Expression targetExpression;
if (requireTarget) {
var mre = new MemberReferenceExpression(target, method.Name);
var mre = new MemberReferenceExpression(target, methodName);
mre.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
mre.WithRR(result);
targetExpression = mre;
} else {
var ide = new IdentifierExpression(method.Name)
var ide = new IdentifierExpression(methodName)
.WithRR(result);
targetExpression = ide;
}
@ -1285,11 +1303,25 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1285,11 +1303,25 @@ namespace ICSharpCode.Decompiler.CSharp
.WithILInstruction(inst)
.WithRR(new ConversionResolveResult(
inst.Method.DeclaringType,
new MemberResolveResult(target.ResolveResult, method),
result,
Conversion.MethodGroupConversion(method, func.OpCode == OpCode.LdVirtFtn, false)));
return oce;
}
static MethodGroupResolveResult ToMethodGroup(IMethod method, ILFunction localFunction)
{
return new MethodGroupResolveResult(
null,
localFunction.Name,
new[] {
new MethodListWithDeclaringType(
method.DeclaringType,
new IParameterizedMember[] { method }
)
}, EmptyList<IType>.Instance
);
}
internal TranslatedExpression CallWithNamedArgs(Block block)
{
Debug.Assert(block.Kind == BlockKind.CallWithNamedArgs);

42
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,35 @@ namespace ICSharpCode.Decompiler.CSharp @@ -189,7 +189,35 @@ 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 f in function.LocalFunctions.OfType<ILFunction>()) {
if (f.Name == name)
return true;
}
return false;
}
}
internal ILFunction ResolveLocalFunction(IMethod method)
{
Debug.Assert(method.IsLocalFunction);
method = method.ReducedFrom;
foreach (var parent in currentFunction.Ancestors.OfType<ILFunction>()) {
var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method == method);
if (definition != null) {
return definition;
}
}
return null;
}
bool RequiresQualifier(IMember member, TranslatedExpression target)
@ -1839,7 +1867,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1839,7 +1867,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;
@ -1848,17 +1876,19 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1848,17 +1876,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, decompilationContext))
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)
{

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

@ -1883,6 +1883,25 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -1883,6 +1883,25 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
Semicolon();
EndNode(variableDeclarationStatement);
}
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);
EndNode(localFunctionDeclarationStatement);
}
public virtual void VisitWhileStatement(WhileStatement whileStatement)
{

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

@ -46,22 +46,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -46,22 +46,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
/// Gets whether a params-Array is being used in its expanded form.
/// </summary>
public readonly bool IsExpandedForm;
readonly IReadOnlyList<int> argumentToParameterMap;
/// <summary>
/// If IsExtensionMethodInvocation is true this property holds the reduced method.
/// </summary>
IMethod reducedMethod;
public IMethod ReducedMethod {
get {
if (!IsExtensionMethodInvocation)
return null;
if (reducedMethod == null && Member is IMethod)
reducedMethod = new ReducedExtensionMethod ((IMethod)Member);
return reducedMethod;
}
}
readonly IReadOnlyList<int> argumentToParameterMap;
public CSharpInvocationResolveResult(
ResolveResult targetResult, IParameterizedMember member,

306
ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs

@ -1,306 +0,0 @@ @@ -1,306 +0,0 @@
//
// ReducedExtensionMethod.cs
//
// Author:
// Mike Krüger <mkrueger@xamarin.com>
//
// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.CSharp.Resolver
{
/// <summary>
/// An invocated extension method hides the extension parameter in its parameter list.
/// It's used to hide the internals of extension method invocation in certain situation to simulate the
/// syntactic way of writing extension methods on semantic level.
/// </summary>
public class ReducedExtensionMethod : IMethod
{
readonly IMethod baseMethod;
public ReducedExtensionMethod(IMethod baseMethod)
{
this.baseMethod = baseMethod;
}
public bool Equals(IMember obj, TypeVisitor typeNormalization)
{
var other = obj as ReducedExtensionMethod;
if (other == null)
return false;
return baseMethod.Equals(other.baseMethod, typeNormalization);
}
public override bool Equals(object obj)
{
var other = obj as ReducedExtensionMethod;
if (other == null)
return false;
return baseMethod.Equals(other.baseMethod);
}
public override int GetHashCode()
{
unchecked {
return baseMethod.GetHashCode() + 1;
}
}
public override string ToString()
{
return string.Format("[ReducedExtensionMethod: ReducedFrom={0}]", ReducedFrom);
}
#region IMember implementation
public IMember MemberDefinition {
get {
return baseMethod.MemberDefinition;
}
}
public IType ReturnType {
get {
return baseMethod.ReturnType;
}
}
public IEnumerable<IMember> ExplicitlyImplementedInterfaceMembers {
get {
return baseMethod.ExplicitlyImplementedInterfaceMembers;
}
}
public bool IsExplicitInterfaceImplementation {
get {
return baseMethod.IsExplicitInterfaceImplementation;
}
}
public bool IsVirtual {
get {
return baseMethod.IsVirtual;
}
}
public bool IsOverride {
get {
return baseMethod.IsOverride;
}
}
public bool IsOverridable {
get {
return baseMethod.IsOverridable;
}
}
public TypeParameterSubstitution Substitution {
get {
return baseMethod.Substitution;
}
}
public IMethod Specialize(TypeParameterSubstitution substitution)
{
return new ReducedExtensionMethod((IMethod)baseMethod.Specialize(substitution));
}
IMember IMember.Specialize(TypeParameterSubstitution substitution)
{
return Specialize(substitution);
}
#endregion
#region IMethod implementation
public IReadOnlyList<ITypeParameter> TypeParameters {
get {
return baseMethod.TypeParameters;
}
}
public bool IsExtensionMethod {
get {
return true;
}
}
public bool IsConstructor {
get {
return baseMethod.IsConstructor;
}
}
public bool IsDestructor {
get {
return baseMethod.IsDestructor;
}
}
public bool IsOperator {
get {
return baseMethod.IsOperator;
}
}
public bool HasBody {
get {
return baseMethod.HasBody;
}
}
public bool IsAccessor => baseMethod.IsAccessor;
public IMember AccessorOwner => baseMethod.AccessorOwner;
public MethodSemanticsAttributes AccessorKind => baseMethod.AccessorKind;
public IMethod ReducedFrom {
get {
return baseMethod;
}
}
public IReadOnlyList<IType> TypeArguments {
get {
return baseMethod.TypeArguments;
}
}
#endregion
#region IParameterizedMember implementation
List<IParameter> parameters;
public IReadOnlyList<IParameter> Parameters {
get {
if (parameters == null)
parameters = new List<IParameter> (baseMethod.Parameters.Skip (1));
return parameters;
}
}
#endregion
#region IEntity implementation
public System.Reflection.Metadata.EntityHandle MetadataToken => baseMethod.MetadataToken;
public SymbolKind SymbolKind {
get {
return baseMethod.SymbolKind;
}
}
public ITypeDefinition DeclaringTypeDefinition {
get {
return baseMethod.DeclaringTypeDefinition;
}
}
public IType DeclaringType {
get {
return baseMethod.DeclaringType;
}
}
public IModule ParentModule {
get {
return baseMethod.ParentModule;
}
}
IEnumerable<IAttribute> IEntity.GetAttributes() => baseMethod.GetAttributes();
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes();
bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly;
public bool IsStatic {
get {
return false;
}
}
public bool IsAbstract {
get {
return baseMethod.IsAbstract;
}
}
public bool IsSealed {
get {
return baseMethod.IsSealed;
}
}
#endregion
#region IHasAccessibility implementation
public Accessibility Accessibility {
get {
return baseMethod.Accessibility;
}
}
#endregion
#region INamedElement implementation
public string FullName {
get {
return baseMethod.FullName;
}
}
public string Name {
get {
return baseMethod.Name;
}
}
public string ReflectionName {
get {
return baseMethod.ReflectionName;
}
}
public string Namespace {
get {
return baseMethod.Namespace;
}
}
#endregion
#region ICompilationProvider implementation
public ICompilation Compilation {
get {
return baseMethod.Compilation;
}
}
#endregion
}
}

48
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
{
@ -725,18 +726,32 @@ namespace ICSharpCode.Decompiler.CSharp @@ -725,18 +726,32 @@ namespace ICSharpCode.Decompiler.CSharp
/// <summary>
/// Determines whether storeInst.Variable is only assigned once and used only inside <paramref name="usingContainer"/>.
/// Loads by reference (ldloca) are only allowed in the context of this pointer in call instructions.
/// Loads by reference (ldloca) are only allowed in the context of this pointer in call instructions,
/// or as target of ldobj.
/// (This only applies to value types.)
/// </summary>
bool VariableIsOnlyUsedInBlock(StLoc storeInst, BlockContainer usingContainer)
{
if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(usingContainer)))
return false;
if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(usingContainer) || !ILInlining.IsUsedAsThisPointerInCall(la) || IsTargetOfSetterCall(la, la.Variable.Type)))
if (storeInst.Variable.AddressInstructions.Any(inst => !AddressUseAllowed(inst)))
return false;
if (storeInst.Variable.StoreInstructions.OfType<ILInstruction>().Any(st => st != storeInst))
return false;
return true;
bool AddressUseAllowed(LdLoca la)
{
if (!la.IsDescendantOf(usingContainer))
return false;
if (ILInlining.IsUsedAsThisPointerInCall(la) && !IsTargetOfSetterCall(la, la.Variable.Type))
return true;
var current = la.Parent;
while (current is LdFlda next) {
current = next.Parent;
}
return current is LdObj;
}
}
/// <summary>
@ -849,6 +864,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -849,6 +864,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (blockStatement.LastOrDefault() is ContinueStatement continueStmt)
continueStmt.Remove();
DeclareLocalFunctions(currentFunction, container, blockStatement);
return new WhileStatement(new PrimitiveExpression(true), blockStatement);
case ContainerKind.While:
continueTarget = container.EntryPoint;
@ -870,6 +886,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -870,6 +886,7 @@ namespace ICSharpCode.Decompiler.CSharp
if (blockStatement.LastOrDefault() is ContinueStatement continueStmt2)
continueStmt2.Remove();
DeclareLocalFunctions(currentFunction, container, blockStatement);
return new WhileStatement(exprBuilder.TranslateCondition(condition), blockStatement);
case ContainerKind.DoWhile:
continueTarget = container.Blocks.Last();
@ -888,6 +905,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -888,6 +905,7 @@ namespace ICSharpCode.Decompiler.CSharp
// to continue statements, we have to introduce an extra label.
blockStatement.Add(new LabelStatement { Label = continueTarget.Label });
}
DeclareLocalFunctions(currentFunction, container, blockStatement);
if (blockStatement.Statements.Count == 0) {
return new WhileStatement {
Condition = exprBuilder.TranslateCondition(condition),
@ -919,6 +937,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -919,6 +937,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
if (continueTarget.IncomingEdgeCount > continueCount)
blockStatement.Add(new LabelStatement { Label = continueTarget.Label });
DeclareLocalFunctions(currentFunction, container, blockStatement);
return forStmt;
default:
throw new ArgumentOutOfRangeException();
@ -927,7 +946,30 @@ namespace ICSharpCode.Decompiler.CSharp @@ -927,7 +946,30 @@ namespace ICSharpCode.Decompiler.CSharp
BlockStatement ConvertBlockContainer(BlockContainer container, bool isLoop)
{
return ConvertBlockContainer(new BlockStatement(), container, container.Blocks, isLoop);
var blockStatement = ConvertBlockContainer(new BlockStatement(), container, container.Blocks, isLoop);
DeclareLocalFunctions(currentFunction, container, blockStatement);
return blockStatement;
}
void DeclareLocalFunctions(ILFunction currentFunction, BlockContainer container, BlockStatement blockStatement)
{
foreach (var localFunction in currentFunction.LocalFunctions.OrderBy(f => f.Name)) {
if (localFunction.DeclarationScope != container)
continue;
blockStatement.Add(TranslateFunction(localFunction));
}
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);
stmt.AddAnnotation(new MemberResolveResult(null, function.ReducedMethod));
return stmt;
}
}
BlockStatement ConvertBlockContainer(BlockStatement blockStatement, BlockContainer container, IEnumerable<Block> blocks, bool isLoop)

21
ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs

@ -390,7 +390,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -390,7 +390,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
VisitChildren (variableDeclarationStatement);
}
public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement)
{
VisitChildren(localFunctionDeclarationStatement);
}
public virtual void VisitWhileStatement (WhileStatement whileStatement)
{
VisitChildren (whileStatement);
@ -1037,7 +1042,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1037,7 +1042,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
return VisitChildren (variableDeclarationStatement);
}
public virtual T VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement)
{
return VisitChildren(localFunctionDeclarationStatement);
}
public virtual T VisitWhileStatement (WhileStatement whileStatement)
{
return VisitChildren (whileStatement);
@ -1684,7 +1694,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1684,7 +1694,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
return VisitChildren (variableDeclarationStatement, data);
}
public virtual S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement, T data)
{
return VisitChildren(localFunctionDeclarationStatement, data);
}
public virtual S VisitWhileStatement (WhileStatement whileStatement, T data)
{
return VisitChildren (whileStatement, data);

3
ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs

@ -110,6 +110,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -110,6 +110,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
void VisitUnsafeStatement(UnsafeStatement unsafeStatement);
void VisitUsingStatement(UsingStatement usingStatement);
void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement);
void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement);
void VisitWhileStatement(WhileStatement whileStatement);
void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement);
void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement);
@ -251,6 +252,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -251,6 +252,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitUnsafeStatement(UnsafeStatement unsafeStatement);
S VisitUsingStatement(UsingStatement usingStatement);
S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement);
S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement);
S VisitWhileStatement(WhileStatement whileStatement);
S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement);
S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement);
@ -392,6 +394,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -392,6 +394,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
S VisitUnsafeStatement(UnsafeStatement unsafeStatement, T data);
S VisitUsingStatement(UsingStatement usingStatement, T data);
S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, T data);
S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement, T data);
S VisitWhileStatement(WhileStatement whileStatement, T data);
S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, T data);
S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement, T data);

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

@ -0,0 +1,110 @@ @@ -0,0 +1,110 @@
// 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 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 CSharpTokenNode RParToken {
get { return GetChildByRole(Roles.RPar); }
}
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)
{
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); }
}
public override void AcceptVisitor(IAstVisitor visitor)
{
visitor.VisitLocalFunctionDeclarationStatement(this);
}
public override T AcceptVisitor<T>(IAstVisitor<T> visitor)
{
return visitor.VisitLocalFunctionDeclarationStatement(this);
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return visitor.VisitLocalFunctionDeclarationStatement(this, data);
}
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);
}
}
}

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

@ -260,7 +260,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -260,7 +260,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} else {
newPoint = new InsertionPoint { level = nodeLevel, nextNode = identExpr };
if (variable.HasInitialValue) {
// Uninitialized variables are logically initialized at the beginning of the functin
// Uninitialized variables are logically initialized at the beginning of the function
// Because it's possible that the variable has a loop-carried dependency,
// declare it outside of any loops.
while (startIndex >= 0) {

13
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -942,22 +942,19 @@ namespace ICSharpCode.Decompiler @@ -942,22 +942,19 @@ namespace ICSharpCode.Decompiler
}
}
bool localFunctions = false;
bool localFunctions = true;
/// <summary>
/// Gets/Sets whether C# 7.0 local functions should be used.
/// Note: this language feature is currently not implemented and this setting is always false.
/// Gets/Sets whether C# 7.0 local functions should be transformed.
/// </summary>
[Category("C# 7.0 / VS 2017")]
[Description("DecompilerSettings.IntroduceLocalFunctionsNOTIMPLEMENTED")]
[Browsable(false)]
[Description("DecompilerSettings.IntroduceLocalFunctions")]
public bool LocalFunctions {
get { return localFunctions; }
set {
if (localFunctions != value) {
throw new NotImplementedException("C# 7.0 local functions are not implemented!");
//localFunctions = value;
//OnPropertyChanged();
localFunctions = value;
OnPropertyChanged();
}
}
}

3
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -165,6 +165,7 @@ @@ -165,6 +165,7 @@
<Compile Include="CSharp\Syntax\Statements\GotoStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\IfElseStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\LabelStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\LocalFunctionDeclarationStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\LockStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\ReturnStatement.cs" />
<Compile Include="CSharp\Syntax\Statements\Statement.cs" />
@ -226,7 +227,7 @@ @@ -226,7 +227,7 @@
<Compile Include="CSharp\Resolver\NameLookupMode.cs" />
<Compile Include="CSharp\Resolver\OverloadResolution.cs" />
<Compile Include="CSharp\Resolver\OverloadResolutionErrors.cs" />
<Compile Include="CSharp\Resolver\ReducedExtensionMethod.cs" />
<Compile Include="TypeSystem\Implementation\LocalFunctionMethod.cs" />
<Compile Include="CSharp\Resolver\TypeInference.cs" />
<Compile Include="CSharp\Transforms\CombineQueryExpressions.cs" />
<Compile Include="CSharp\Transforms\FlattenSwitchBlocks.cs" />

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

@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
methodTypeParameters: null);
var body = context.TypeSystem.MainModule.PEFile.Reader.GetMethodBody(methodDef.RelativeVirtualAddress);
var il = context.CreateILReader()
.ReadIL(method, body, genericContext, context.CancellationToken);
.ReadIL(method, body, genericContext, context.Function.Kind, context.CancellationToken);
il.RunTransforms(CSharpDecompiler.EarlyILTransforms(true),
new ILTransformContext(il, context.TypeSystem, context.DebugInfo, context.Settings) {
CancellationToken = context.CancellationToken,

4
ICSharpCode.Decompiler/IL/ILReader.cs

@ -480,14 +480,14 @@ namespace ICSharpCode.Decompiler.IL @@ -480,14 +480,14 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>
/// Decodes the specified method body and returns an ILFunction.
/// </summary>
public ILFunction ReadIL(MethodDefinitionHandle method, MethodBodyBlock body, GenericContext genericContext = default, CancellationToken cancellationToken = default)
public ILFunction ReadIL(MethodDefinitionHandle method, MethodBodyBlock body, GenericContext genericContext = default, ILFunctionKind kind = ILFunctionKind.TopLevelFunction, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
Init(method, body, genericContext);
ReadInstructions(cancellationToken);
var blockBuilder = new BlockBuilder(body, variableByExceptionHandler);
blockBuilder.CreateBlocks(mainContainer, instructionBuilder, isBranchTarget, cancellationToken);
var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer);
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);

3
ICSharpCode.Decompiler/IL/ILVariable.cs

@ -415,7 +415,8 @@ namespace ICSharpCode.Decompiler.IL @@ -415,7 +415,8 @@ namespace ICSharpCode.Decompiler.IL
output.Write(" init");
}
if (CaptureScope != null) {
output.Write(" captured in " + CaptureScope.EntryPoint.Label);
output.Write(" captured in ");
output.WriteLocalReference(CaptureScope.EntryPoint.Label, CaptureScope);
}
if (StateMachineField != null) {
output.Write(" from state-machine");

47
ICSharpCode.Decompiler/IL/Instructions.cs

@ -476,7 +476,7 @@ namespace ICSharpCode.Decompiler.IL @@ -476,7 +476,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -491,7 +491,7 @@ namespace ICSharpCode.Decompiler.IL @@ -491,7 +491,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (CallInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()
@ -742,9 +742,11 @@ namespace ICSharpCode.Decompiler.IL @@ -742,9 +742,11 @@ namespace ICSharpCode.Decompiler.IL
SetChildInstruction(ref this.body, value, 0);
}
}
public static readonly SlotInfo LocalFunctionsSlot = new SlotInfo("LocalFunctions");
public InstructionCollection<ILFunction> LocalFunctions { get; private set; }
protected sealed override int GetChildCount()
{
return 1;
return 1 + LocalFunctions.Count;
}
protected sealed override ILInstruction GetChild(int index)
{
@ -752,7 +754,7 @@ namespace ICSharpCode.Decompiler.IL @@ -752,7 +754,7 @@ namespace ICSharpCode.Decompiler.IL
case 0:
return this.body;
default:
throw new IndexOutOfRangeException();
return this.LocalFunctions[index - 1];
}
}
protected sealed override void SetChild(int index, ILInstruction value)
@ -762,7 +764,8 @@ namespace ICSharpCode.Decompiler.IL @@ -762,7 +764,8 @@ namespace ICSharpCode.Decompiler.IL
this.Body = value;
break;
default:
throw new IndexOutOfRangeException();
this.LocalFunctions[index - 1] = (ILFunction)value;
break;
}
}
protected sealed override SlotInfo GetChildSlot(int index)
@ -771,13 +774,15 @@ namespace ICSharpCode.Decompiler.IL @@ -771,13 +774,15 @@ namespace ICSharpCode.Decompiler.IL
case 0:
return BodySlot;
default:
throw new IndexOutOfRangeException();
return LocalFunctionsSlot;
}
}
public sealed override ILInstruction Clone()
{
var clone = (ILFunction)ShallowClone();
clone.Body = this.body.Clone();
clone.LocalFunctions = new InstructionCollection<ILFunction>(clone, 1);
clone.LocalFunctions.AddRange(this.LocalFunctions.Select(arg => (ILFunction)arg.Clone()));
clone.CloneVariables();
return clone;
}
@ -797,7 +802,7 @@ namespace ICSharpCode.Decompiler.IL @@ -797,7 +802,7 @@ namespace ICSharpCode.Decompiler.IL
protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match)
{
var o = other as ILFunction;
return o != null && this.body.PerformMatch(o.body, ref match);
return o != null && this.body.PerformMatch(o.body, ref match) && Patterns.ListMatch.DoMatch(this.LocalFunctions, o.LocalFunctions, ref match);
}
}
}
@ -4254,7 +4259,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4254,7 +4259,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Indices[index - 0] = value;
this.Indices[index - 0] = (ILInstruction)value;
break;
}
}
@ -4269,7 +4274,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4269,7 +4274,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (NewArr)ShallowClone();
clone.Indices = new InstructionCollection<ILInstruction>(clone, 0);
clone.Indices.AddRange(this.Indices.Select(arg => arg.Clone()));
clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
public override StackType ResultType { get { return StackType.O; } }
@ -4607,7 +4612,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4607,7 +4612,7 @@ namespace ICSharpCode.Decompiler.IL
this.Array = value;
break;
default:
this.Indices[index - 1] = value;
this.Indices[index - 1] = (ILInstruction)value;
break;
}
}
@ -4625,7 +4630,7 @@ namespace ICSharpCode.Decompiler.IL @@ -4625,7 +4630,7 @@ namespace ICSharpCode.Decompiler.IL
var clone = (LdElema)ShallowClone();
clone.Array = this.array.Clone();
clone.Indices = new InstructionCollection<ILInstruction>(clone, 1);
clone.Indices.AddRange(this.Indices.Select(arg => arg.Clone()));
clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
public bool DelayExceptions; // NullReferenceException/IndexOutOfBoundsException only occurs when the reference is dereferenced
@ -5578,7 +5583,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5578,7 +5583,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -5593,7 +5598,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5593,7 +5598,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (DynamicGetIndexInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()
@ -5646,7 +5651,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5646,7 +5651,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -5661,7 +5666,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5661,7 +5666,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (DynamicSetIndexInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()
@ -5714,7 +5719,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5714,7 +5719,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -5729,7 +5734,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5729,7 +5734,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (DynamicInvokeMemberInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()
@ -5782,7 +5787,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5782,7 +5787,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -5797,7 +5802,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5797,7 +5802,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (DynamicInvokeConstructorInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()
@ -5850,7 +5855,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5850,7 +5855,7 @@ namespace ICSharpCode.Decompiler.IL
{
switch (index) {
default:
this.Arguments[index - 0] = value;
this.Arguments[index - 0] = (ILInstruction)value;
break;
}
}
@ -5865,7 +5870,7 @@ namespace ICSharpCode.Decompiler.IL @@ -5865,7 +5870,7 @@ namespace ICSharpCode.Decompiler.IL
{
var clone = (DynamicInvokeInstruction)ShallowClone();
clone.Arguments = new InstructionCollection<ILInstruction>(clone, 0);
clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone()));
clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone()));
return clone;
}
protected override InstructionFlags ComputeFlags()

14
ICSharpCode.Decompiler/IL/Instructions.tt

@ -49,7 +49,8 @@ @@ -49,7 +49,8 @@
VoidResult, NoArguments, CustomWriteTo),
new OpCode("ILFunction", "A container of IL blocks.",
CustomChildren(new [] {
new ChildInfo("body")
new ChildInfo("body"),
new ChildInfo("localFunctions") { IsCollection = true, Type = "ILFunction" }
}), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("O")
),
new OpCode("BlockContainer", "A container of IL blocks.",
@ -773,6 +774,7 @@ namespace ICSharpCode.Decompiler.IL @@ -773,6 +774,7 @@ namespace ICSharpCode.Decompiler.IL
public readonly string SlotName;
public bool IsCollection;
public string Type = "ILInstruction";
public bool CanInlineInto;
public string[] ExpectedTypes;
@ -818,7 +820,7 @@ namespace ICSharpCode.Decompiler.IL @@ -818,7 +820,7 @@ namespace ICSharpCode.Decompiler.IL
childCount = children.Length - 1;
opCode.Flags.Add(argProp + ".Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags)");
opCode.ConstructorParameters.Add("params ILInstruction[] " + arg);
opCode.ConstructorBody.Add("this." + argProp + " = new InstructionCollection<ILInstruction>(this, " + i + ");");
opCode.ConstructorBody.Add("this." + argProp + " = new InstructionCollection<" + children[i].Type + ">(this, " + i + ");");
opCode.ConstructorBody.Add("this." + argProp + ".AddRange(" + arg + ");");
opCode.PerformMatchConditions.Add("Patterns.ListMatch.DoMatch(this." + argProp + ", o." + argProp + ", ref match)");
if (i == 0)
@ -831,7 +833,7 @@ namespace ICSharpCode.Decompiler.IL @@ -831,7 +833,7 @@ namespace ICSharpCode.Decompiler.IL
opCode.WriteArguments.Add("\t" + arg + ".WriteTo(output, options);");
opCode.WriteArguments.Add("}");
opCode.Members.Add("public static readonly SlotInfo " + children[i].SlotName + " = " + children[i].GetSlotInit() + ";");
opCode.Members.Add("public InstructionCollection<ILInstruction> " + argProp + " { get; private set; }");
opCode.Members.Add("public InstructionCollection<" + children[i].Type + "> " + argProp + " { get; private set; }");
} else {
opCode.Flags.Add(arg + ".Flags");
opCode.ConstructorParameters.Add("ILInstruction " + arg);
@ -910,7 +912,7 @@ namespace ICSharpCode.Decompiler.IL @@ -910,7 +912,7 @@ namespace ICSharpCode.Decompiler.IL
if (collection == null)
b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();");
else {
b.AppendLine("\t\t\tthis." + collection.PropertyName + "[index - " + childCount + "] = value;");
b.AppendLine("\t\t\tthis." + collection.PropertyName + "[index - " + childCount + "] = (" + collection.Type + ")value;");
b.AppendLine("\t\t\tbreak;");
}
b.AppendLine("\t}");
@ -940,8 +942,8 @@ namespace ICSharpCode.Decompiler.IL @@ -940,8 +942,8 @@ namespace ICSharpCode.Decompiler.IL
b.AppendLine("\tvar clone = (" + opCode.Name + ")ShallowClone();");
for (int i = 0; i < children.Length; i++) {
if (children[i].IsCollection) {
b.AppendLine("\tclone." + children[i].PropertyName + " = new InstructionCollection<ILInstruction>(clone, " + i + ");");
b.AppendLine("\tclone." + children[i].PropertyName + ".AddRange(this." + children[i].PropertyName + ".Select(arg => arg.Clone()));");
b.AppendLine("\tclone." + children[i].PropertyName + " = new InstructionCollection<" + children[i].Type + ">(clone, " + i + ");");
b.AppendLine("\tclone." + children[i].PropertyName + ".AddRange(this." + children[i].PropertyName + ".Select(arg => (" + children[i].Type + ")arg.Clone()));");
} else {
b.AppendLine("\tclone." + children[i].PropertyName + " = this." + children[i].Name + ".Clone();");
}

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

@ -18,25 +18,60 @@ @@ -18,25 +18,60 @@
using System;
using System.Collections.Generic;
using ICSharpCode.Decompiler.IL.Transforms;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using System.Diagnostics;
namespace ICSharpCode.Decompiler.IL
{
partial class ILFunction
{
/// <summary>
/// Gets the method definition from metadata.
/// May be null for functions that were not constructed from metadata,
/// e.g., expression trees.
/// </summary>
public readonly IMethod Method;
/// <summary>
/// Gets the generic context of this function.
/// </summary>
public readonly GenericContext GenericContext;
/// <summary>
/// Gets the name of this function, usually this returns the name from metadata.
/// <para>
/// For local functions:
/// This is the name that is used to declare and use the function.
/// It may not conflict with the names of local variables of ancestor functions
/// and may be overwritten by the AssignVariableNames step.
///
/// For top-level functions, delegates and expressions trees modifying this usually
/// has no effect, as the name should not be used in the final AST construction.
/// </para>
/// </summary>
public string Name;
/// <summary>
/// Size of the IL code in this function.
/// Note: after async/await transform, this is the code size of the MoveNext function.
/// </summary>
public int CodeSize;
/// <summary>
/// List of ILVariables used in this function.
/// </summary>
public readonly ILVariableCollection Variables;
/// <summary>
/// Gets the scope in which the local function is declared.
/// Returns null, if this is not a local function.
/// </summary>
public BlockContainer DeclarationScope { get; internal set; }
/// <summary>
/// List of warnings of ILReader.
/// </summary>
@ -51,6 +86,9 @@ namespace ICSharpCode.Decompiler.IL @@ -51,6 +86,9 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public bool IsIterator;
/// <summary>
/// Gets whether the YieldReturnDecompiler determined that the Mono C# compiler was used to compile this function.
/// </summary>
public bool StateMachineCompiledWithMono;
/// <summary>
@ -70,6 +108,11 @@ namespace ICSharpCode.Decompiler.IL @@ -70,6 +108,11 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public IMethod MoveNextMethod;
/// <summary>
/// If this function is a local function, this field stores the reduced version of the function.
/// </summary>
internal TypeSystem.Implementation.LocalFunctionMethod ReducedMethod;
internal DebugInfo.AsyncDebugInfo AsyncDebugInfo;
int ctorCallStart = int.MinValue;
@ -96,37 +139,99 @@ namespace ICSharpCode.Decompiler.IL @@ -96,37 +139,99 @@ namespace ICSharpCode.Decompiler.IL
/// <summary>
/// If this is an expression tree or delegate, returns the expression tree type Expression{T} or T.
/// T is the delegate type that matches the signature of this method.
/// Otherwise this must be null.
/// </summary>
public IType DelegateType;
public bool IsExpressionTree => DelegateType != null && DelegateType.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1;
ILFunctionKind kind;
/// <summary>
/// Gets which kind of function this is.
/// </summary>
public ILFunctionKind Kind {
get => kind;
internal set {
if (kind == ILFunctionKind.TopLevelFunction || kind == ILFunctionKind.LocalFunction)
throw new InvalidOperationException("ILFunction.Kind of a top-level or local function may not be changed.");
kind = value;
}
}
/// <summary>
/// Return type of this function.
/// Might be null, if this function was not created from metadata.
/// </summary>
public readonly IType ReturnType;
/// <summary>
/// List of parameters of this function.
/// Might be null, if this function was not created from metadata.
/// </summary>
public readonly IReadOnlyList<IParameter> Parameters;
public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body) : base(OpCode.ILFunction)
/// <summary>
/// Constructs a new ILFunction from the given metadata and with the given ILAst body.
/// </summary>
/// <remarks>
/// Use <see cref="ILReader"/> to create ILAst.
/// <paramref name="method"/> may be null.
/// </remarks>
public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body, ILFunctionKind kind = ILFunctionKind.TopLevelFunction) : base(OpCode.ILFunction)
{
this.Method = method;
this.Name = Method?.Name;
this.CodeSize = codeSize;
this.GenericContext = genericContext;
this.Body = body;
this.ReturnType = Method?.ReturnType;
this.Parameters = Method?.Parameters;
this.Variables = new ILVariableCollection(this);
this.LocalFunctions = new InstructionCollection<ILFunction>(this, 1);
this.kind = kind;
}
public ILFunction(IType returnType, IReadOnlyList<IParameter> parameters, GenericContext genericContext, ILInstruction body) : base(OpCode.ILFunction)
/// <summary>
/// This constructor is only to be used by the TransformExpressionTrees step.
/// </summary>
internal ILFunction(IType returnType, IReadOnlyList<IParameter> parameters, GenericContext genericContext, ILInstruction body) : base(OpCode.ILFunction)
{
this.GenericContext = genericContext;
this.Body = body;
this.ReturnType = returnType;
this.Parameters = parameters;
this.Variables = new ILVariableCollection(this);
this.LocalFunctions = new InstructionCollection<ILFunction>(this, 1);
this.kind = ILFunctionKind.ExpressionTree;
}
internal override void CheckInvariant(ILPhase phase)
{
switch (kind) {
case ILFunctionKind.TopLevelFunction:
Debug.Assert(Parent == null);
Debug.Assert(DelegateType == null);
Debug.Assert(DeclarationScope == null);
Debug.Assert(Method != null);
break;
case ILFunctionKind.Delegate:
Debug.Assert(Parent != null && !(Parent is Block));
Debug.Assert(DelegateType != null);
Debug.Assert(DeclarationScope == null);
Debug.Assert(!(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1));
break;
case ILFunctionKind.ExpressionTree:
Debug.Assert(Parent != null && !(Parent is Block));
Debug.Assert(DelegateType != null);
Debug.Assert(DeclarationScope == null);
Debug.Assert(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1);
break;
case ILFunctionKind.LocalFunction:
Debug.Assert(Parent is ILFunction);
Debug.Assert(DeclarationScope != null);
Debug.Assert(DelegateType == null);
Debug.Assert(Method != null);
break;
}
for (int i = 0; i < Variables.Count; i++) {
Debug.Assert(Variables[i].Function == this);
Debug.Assert(Variables[i].IndexInFunction == i);
@ -148,8 +253,13 @@ namespace ICSharpCode.Decompiler.IL @@ -148,8 +253,13 @@ namespace ICSharpCode.Decompiler.IL
output.Write(' ');
Method.WriteTo(output);
}
if (IsExpressionTree) {
output.Write(".ET");
switch (kind) {
case ILFunctionKind.ExpressionTree:
output.Write(".ET");
break;
case ILFunctionKind.LocalFunction:
output.Write(".local");
break;
}
if (DelegateType != null) {
output.Write("[");
@ -165,6 +275,11 @@ namespace ICSharpCode.Decompiler.IL @@ -165,6 +275,11 @@ namespace ICSharpCode.Decompiler.IL
if (IsIterator) {
output.WriteLine(".iterator");
}
if (DeclarationScope != null) {
output.Write("declared as " + Name + " in ");
output.WriteLocalReference(DeclarationScope.EntryPoint.Label, DeclarationScope);
output.WriteLine();
}
output.MarkFoldStart(Variables.Count + " variable(s)", true);
foreach (var variable in Variables) {
@ -181,6 +296,11 @@ namespace ICSharpCode.Decompiler.IL @@ -181,6 +296,11 @@ namespace ICSharpCode.Decompiler.IL
body.WriteTo(output, options);
output.WriteLine();
foreach (var localFunction in LocalFunctions) {
output.WriteLine();
localFunction.WriteTo(output, options);
}
if (options.ShowILRanges) {
var unusedILRanges = FindUnusedILRanges();
if (!unusedILRanges.IsEmpty) {
@ -292,4 +412,33 @@ namespace ICSharpCode.Decompiler.IL @@ -292,4 +412,33 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(ok);
}
}
public enum ILFunctionKind
{
/// <summary>
/// ILFunction is a "top-level" function, i.e., method, accessor, constructor, destructor or operator.
/// </summary>
TopLevelFunction,
/// <summary>
/// ILFunction is a delegate or lambda expression.
/// </summary>
/// <remarks>
/// This kind is introduced by the DelegateConstruction and TransformExpressionTrees steps in the decompiler pipeline.
/// </remarks>
Delegate,
/// <summary>
/// ILFunction is an expression tree lambda.
/// </summary>
/// <remarks>
/// This kind is introduced by the TransformExpressionTrees step in the decompiler pipeline.
/// </remarks>
ExpressionTree,
/// <summary>
/// ILFunction is a C# 7.0 local function.
/// </summary>
/// <remarks>
/// This kind is introduced by the LocalFunctionDecompiler step in the decompiler pipeline.
/// </remarks>
LocalFunction
}
}

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

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

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

@ -24,7 +24,10 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -24,7 +24,10 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL.Transforms
{
public class DelegateConstruction : IILTransform
/// <summary>
///
/// </summary>
class DelegateConstruction : IILTransform
{
ILTransformContext context;
ITypeResolveContext decompilationContext;
@ -121,11 +124,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -121,11 +124,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var targetMethod = ((IInstructionWithMethodOperand)value.Arguments[1]).Method;
if (!IsAnonymousMethod(decompilationContext.CurrentTypeDefinition, targetMethod))
return null;
if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod.ParentModule.PEFile, (MethodDefinitionHandle)targetMethod.MetadataToken))
return null;
target = value.Arguments[0];
if (targetMethod.MetadataToken.IsNil)
return null;
if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod, context))
return null;
target = value.Arguments[0];
var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken);
if (!methodDefinition.HasBody())
return null;
@ -134,12 +137,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -134,12 +137,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return null;
var ilReader = context.CreateILReader();
var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress);
var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, context.CancellationToken);
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)) {
@ -169,7 +172,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -169,7 +172,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Replaces loads of 'this' with the target expression.
/// Async delegates use: ldobj(ldloca this).
/// </summary>
class ReplaceDelegateTargetVisitor : ILVisitor
internal class ReplaceDelegateTargetVisitor : ILVisitor
{
readonly ILVariable thisVariable;
readonly ILInstruction target;

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

@ -66,6 +66,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -66,6 +66,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitLdObj(LdObj inst)
{
base.VisitLdObj(inst);
AddressOfLdLocToLdLoca(inst, context);
LdObjToLdLoc(inst, context);
}
@ -83,6 +84,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -83,6 +84,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
}
internal static void AddressOfLdLocToLdLoca(LdObj inst, ILTransformContext context)
{
// ldobj(...(addressof(ldloc V))) where ... can be zero or more ldflda instructions
// =>
// ldobj(...(ldloca V))
var temp = inst.Target;
var range = temp.ILRanges;
while (temp.MatchLdFlda(out var ldfldaTarget, out _)) {
temp = ldfldaTarget;
range = range.Concat(temp.ILRanges);
}
if (temp.MatchAddressOf(out var addressOfTarget) && addressOfTarget.MatchLdLoc(out var v)) {
context.Step($"ldobj(...(addressof(ldloca {v.Name}))) => ldobj(...(ldloca {v.Name}))", inst);
var replacement = new LdLoca(v).WithILRange(addressOfTarget);
foreach (var r in range) {
replacement = replacement.WithILRange(r);
}
temp.ReplaceWith(replacement);
}
}
protected internal override void VisitCall(Call inst)
{
var expr = HandleCall(inst, context);

1
ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs

@ -393,6 +393,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -393,6 +393,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
protected internal override void VisitLdObj(LdObj inst)
{
base.VisitLdObj(inst);
EarlyExpressionTransforms.AddressOfLdLocToLdLoca(inst, context);
EarlyExpressionTransforms.LdObjToLdLoc(inst, context);
}

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

@ -1,24 +1,292 @@ @@ -1,24 +1,292 @@
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.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Text;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL.Transforms
{
/// <summary>
/// Decompiler step for C# 7.0 local functions
/// </summary>
class LocalFunctionDecompiler : IILTransform
{
ILTransformContext context;
ITypeResolveContext resolveContext;
struct LocalFunctionInfo
{
public List<CallInstruction> UseSites;
public ILFunction Definition;
}
/// <summary>
/// The transform works like this:
///
/// <para>
/// local functions can either be used in method calls, i.e., call and callvirt instructions,
/// or can be used as part of the "delegate construction" pattern, i.e., <c>newobj Delegate(&lt;target-expression&gt;, ldftn &lt;method&gt;)</c>.
/// </para>
/// As local functions can be declared practically anywhere, we have to take a look at all use-sites and infer the declaration location from that. Use-sites can be call, callvirt and ldftn instructions.
/// After all use-sites are collected we construct the ILAst of the local function and add it to the parent function.
/// Then all use-sites of the local-function are transformed to a call to the <c>LocalFunctionMethod</c> or a ldftn of the <c>LocalFunctionMethod</c>.
/// In a next step we handle all nested local functions.
/// After all local functions are transformed, we move all local functions that capture any variables to their respective declaration scope.
/// </summary>
public void Run(ILFunction function, ILTransformContext context)
{
throw new NotImplementedException();
if (!context.Settings.LocalFunctions)
return;
this.context = context;
this.resolveContext = new SimpleTypeResolveContext(function.Method);
var localFunctions = new Dictionary<MethodDefinitionHandle, LocalFunctionInfo>();
var cancellationToken = context.CancellationToken;
// Find all local functions declared inside this method, including nested local functions or local functions declared in lambdas.
FindUseSites(function, context, localFunctions);
foreach (var (method, info) in localFunctions) {
cancellationToken.ThrowIfCancellationRequested();
var firstUseSite = info.UseSites[0];
context.StepStartGroup($"Transform " + info.Definition.Name, info.Definition);
try {
var localFunction = info.Definition;
if (!localFunction.Method.IsStatic) {
var target = firstUseSite.Arguments[0];
context.Step($"Replace 'this' with {target}", localFunction);
var thisVar = localFunction.Variables.SingleOrDefault(VariableKindExtensions.IsThis);
localFunction.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar));
}
foreach (var useSite in info.UseSites) {
context.Step("Transform use site at " + useSite.StartILOffset, useSite);
if (useSite.OpCode == OpCode.NewObj) {
TransformToLocalFunctionReference(localFunction, useSite);
} else {
DetermineCaptureAndDeclarationScope(localFunction, useSite);
TransformToLocalFunctionInvocation(localFunction.ReducedMethod, useSite);
}
}
if (localFunction.DeclarationScope == null) {
localFunction.DeclarationScope = (BlockContainer)function.Body;
} else if (localFunction.DeclarationScope != function.Body && localFunction.DeclarationScope.Parent is ILFunction declaringFunction) {
function.LocalFunctions.Remove(localFunction);
declaringFunction.LocalFunctions.Add(localFunction);
}
} finally {
context.StepEndGroup();
}
}
}
void FindUseSites(ILFunction function, ILTransformContext context, Dictionary<MethodDefinitionHandle, LocalFunctionInfo> localFunctions)
{
foreach (var inst in function.Body.Descendants) {
context.CancellationToken.ThrowIfCancellationRequested();
if (inst is CallInstruction call && !call.Method.IsLocalFunction && IsLocalFunctionMethod(call.Method, context)) {
HandleUseSite(call.Method, call);
} else if (inst is LdFtn ldftn && !ldftn.Method.IsLocalFunction && ldftn.Parent is NewObj newObj && IsLocalFunctionMethod(ldftn.Method, context) && DelegateConstruction.IsDelegateConstruction(newObj)) {
HandleUseSite(ldftn.Method, newObj);
}
}
void HandleUseSite(IMethod targetMethod, CallInstruction inst)
{
if (!localFunctions.TryGetValue((MethodDefinitionHandle)targetMethod.MetadataToken, out var info)) {
context.StepStartGroup($"Read local function '{targetMethod.Name}'", inst);
info = new LocalFunctionInfo() {
UseSites = new List<CallInstruction>() { inst },
Definition = ReadLocalFunctionDefinition(context.Function, targetMethod)
};
localFunctions.Add((MethodDefinitionHandle)targetMethod.MetadataToken, info);
FindUseSites(info.Definition, context, localFunctions);
context.StepEndGroup();
} else {
info.UseSites.Add(inst);
}
}
}
public static bool IsLocalFunctionMethod(PEFile module, MethodDefinitionHandle methodHandle)
ILFunction ReadLocalFunctionDefinition(ILFunction rootFunction, IMethod targetMethod)
{
var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken);
if (!methodDefinition.HasBody())
return null;
var genericContext = DelegateConstruction.GenericContextFromTypeArguments(targetMethod.Substitution);
if (genericContext == null)
return null;
var ilReader = context.CreateILReader();
var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress);
var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.LocalFunction, context.CancellationToken);
// Embed the local function into the parent function's ILAst, so that "Show steps" can show
// how the local function body is being transformed.
rootFunction.LocalFunctions.Add(function);
function.DeclarationScope = (BlockContainer)rootFunction.Body;
function.CheckInvariant(ILPhase.Normal);
var nestedContext = new ILTransformContext(context, function);
function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext);
function.DeclarationScope = null;
function.ReducedMethod = ReduceToLocalFunction(targetMethod);
return function;
}
static T FindCommonAncestorInstruction<T>(ILInstruction a, ILInstruction b)
where T : ILInstruction
{
var ancestorsOfB = new HashSet<T>(b.Ancestors.OfType<T>());
return a.Ancestors.OfType<T>().FirstOrDefault(ancestorsOfB.Contains);
}
internal static bool IsClosureParameter(IParameter parameter, ITypeResolveContext context)
{
if (!parameter.IsRef)
return false;
var type = ((ByReferenceType)parameter.Type).ElementType.GetDefinition();
return type != null
&& type.Kind == TypeKind.Struct
&& TransformDisplayClassUsage.IsPotentialClosure(context.CurrentTypeDefinition, type);
}
static IType UnwrapByRef(IType type)
{
if (type is ByReferenceType byRef) {
type = byRef.ElementType;
}
return type;
}
internal static ILInstruction GetStatement(ILInstruction inst)
{
while (inst.Parent != null) {
if (inst.Parent is Block b && b.Kind == BlockKind.ControlFlow)
return inst;
inst = inst.Parent;
}
return inst;
}
LocalFunctionMethod ReduceToLocalFunction(IMethod method)
{
int parametersToRemove = 0;
for (int i = method.Parameters.Count - 1; i >= 0; i--) {
if (!IsClosureParameter(method.Parameters[i], resolveContext))
break;
parametersToRemove++;
}
return new LocalFunctionMethod(method, parametersToRemove);
}
static void TransformToLocalFunctionReference(ILFunction function, CallInstruction useSite)
{
useSite.Arguments[0].ReplaceWith(new LdNull().WithILRange(useSite.Arguments[0]));
var fnptr = (IInstructionWithMethodOperand)useSite.Arguments[1];
var replacement = new LdFtn(function.ReducedMethod).WithILRange((ILInstruction)fnptr);
useSite.Arguments[1].ReplaceWith(replacement);
}
void TransformToLocalFunctionInvocation(LocalFunctionMethod reducedMethod, CallInstruction useSite)
{
bool wasInstanceCall = !useSite.Method.IsStatic;
var replacement = new Call(reducedMethod);
int firstArgumentIndex = wasInstanceCall ? 1 : 0;
int argumentCount = useSite.Arguments.Count;
int reducedArgumentCount = argumentCount - (reducedMethod.NumberOfCompilerGeneratedParameters + firstArgumentIndex);
replacement.Arguments.AddRange(useSite.Arguments.Skip(firstArgumentIndex).Take(reducedArgumentCount));
// copy flags
replacement.ConstrainedTo = useSite.ConstrainedTo;
replacement.ILStackWasEmpty = useSite.ILStackWasEmpty;
replacement.IsTail = useSite.IsTail;
// copy IL ranges
replacement.AddILRange(useSite);
if (wasInstanceCall) {
replacement.AddILRange(useSite.Arguments[0]);
}
for (int i = 0; i < reducedMethod.NumberOfCompilerGeneratedParameters; i++) {
replacement.AddILRange(useSite.Arguments[argumentCount - i - 1]);
}
useSite.ReplaceWith(replacement);
}
void DetermineCaptureAndDeclarationScope(ILFunction function, CallInstruction useSite)
{
int firstArgumentIndex = function.Method.IsStatic ? 0 : 1;
for (int i = useSite.Arguments.Count - 1; i >= 0; i--) {
var arg = useSite.Arguments[i];
ILVariable closureVar;
if (!(arg.MatchLdLoc(out closureVar) || arg.MatchLdLoca(out closureVar)))
break;
if (closureVar.Kind == VariableKind.NamedArgument)
break;
if (!TransformDisplayClassUsage.IsPotentialClosure(context, UnwrapByRef(closureVar.Type).GetDefinition()))
break;
if (i - firstArgumentIndex >= 0) {
Debug.Assert(i - firstArgumentIndex < function.Method.Parameters.Count && IsClosureParameter(function.Method.Parameters[i - firstArgumentIndex], resolveContext));
}
if (closureVar.AddressCount == 0 && closureVar.StoreInstructions.Count == 0)
continue;
// determine the capture scope of closureVar and the declaration scope of the function
var instructions = closureVar.StoreInstructions.OfType<ILInstruction>()
.Concat(closureVar.AddressInstructions).OrderBy(inst => inst.StartILOffset);
var additionalScope = BlockContainer.FindClosestContainer(instructions.First());
if (closureVar.CaptureScope == null)
closureVar.CaptureScope = additionalScope;
else
closureVar.CaptureScope = FindCommonAncestorInstruction<BlockContainer>(closureVar.CaptureScope, additionalScope);
if (function.DeclarationScope == null)
function.DeclarationScope = closureVar.CaptureScope;
else
function.DeclarationScope = FindCommonAncestorInstruction<BlockContainer>(function.DeclarationScope, closureVar.CaptureScope);
}
}
internal static bool IsLocalFunctionReference(NewObj inst, ILTransformContext context)
{
if (inst == null || inst.Arguments.Count != 2 || inst.Method.DeclaringType.Kind != TypeKind.Delegate)
return false;
var opCode = inst.Arguments[1].OpCode;
return opCode == OpCode.LdFtn
&& IsLocalFunctionMethod(((IInstructionWithMethodOperand)inst.Arguments[1]).Method, context);
}
public static bool IsLocalFunctionMethod(IMethod method, ILTransformContext context)
{
if (method.MetadataToken.IsNil)
return false;
return IsLocalFunctionMethod(method.ParentModule.PEFile, (MethodDefinitionHandle)method.MetadataToken, context);
}
public static bool IsLocalFunctionMethod(PEFile module, MethodDefinitionHandle methodHandle, ILTransformContext context = null)
{
if (context != null && context.PEFile != module)
return false;
var metadata = module.Metadata;
var method = metadata.GetMethodDefinition(methodHandle);
var declaringType = method.GetDeclaringType();
@ -32,12 +300,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -32,12 +300,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return true;
}
public static bool IsLocalFunctionDisplayClass(PEFile module, TypeDefinitionHandle typeHandle)
public static bool IsLocalFunctionDisplayClass(PEFile module, TypeDefinitionHandle typeHandle, ILTransformContext context = null)
{
if (context != null && context.PEFile != module)
return false;
var metadata = module.Metadata;
var type = metadata.GetTypeDefinition(typeHandle);
if ((type.Attributes & TypeAttributes.NestedPrivate) == 0)
if ((type.Attributes & TypeAttributes.VisibilityMask) != TypeAttributes.NestedPrivate)
return false;
if (!type.HasGeneratedName(metadata))
return false;
@ -46,7 +317,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -46,7 +317,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var declaringType = metadata.GetTypeDefinition(declaringTypeHandle);
foreach (var method in declaringType.GetMethods()) {
if (!IsLocalFunctionMethod(module, method))
if (!IsLocalFunctionMethod(module, method, context))
continue;
var md = metadata.GetMethodDefinition(method);
if (md.DecodeSignature(new FindTypeDecoder(typeHandle), default).ParameterTypes.Any())
@ -60,9 +331,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -60,9 +331,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// Newer Roslyn versions use the format "&lt;callerName&gt;g__functionName|x_y"
/// Older versions use "&lt;callerName&gt;g__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;
@ -76,7 +347,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -76,7 +347,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
struct FindTypeDecoder : ISignatureTypeProvider<bool, Unit>
{
TypeDefinitionHandle handle;
readonly TypeDefinitionHandle handle;
public FindTypeDecoder(TypeDefinitionHandle handle)
{

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

@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// partially copied from CSharpDecompiler
var ilReader = context.CreateILReader();
var body = context.PEFile.Reader.GetMethodBody(methodDef.RelativeVirtualAddress);
var proxyFunction = ilReader.ReadIL(handle, body, genericContext.Value, context.CancellationToken);
var proxyFunction = ilReader.ReadIL(handle, body, genericContext.Value, ILFunctionKind.TopLevelFunction, context.CancellationToken);
var transformContext = new ILTransformContext(context, proxyFunction);
proxyFunction.RunTransforms(CSharp.CSharpDecompiler.EarlyILTransforms(), transformContext);
if (!(proxyFunction.Body is BlockContainer blockContainer))

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

@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
/// <summary>
/// Transforms closure fields to local variables.
///
/// This is a post-processing step of <see cref="DelegateConstruction"/> and <see cref="TransformExpressionTrees"/>.
/// This is a post-processing step of <see cref="DelegateConstruction"/>, <see cref="LocalFunctionDecompiler"/> and <see cref="TransformExpressionTrees"/>.
/// </summary>
class TransformDisplayClassUsage : ILVisitor, IILTransform
{
@ -39,6 +39,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -39,6 +39,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
public ITypeDefinition Definition;
public Dictionary<IField, DisplayClassVariable> Variables;
public BlockContainer CaptureScope;
public ILFunction DeclaringFunction;
}
struct DisplayClassVariable
@ -65,20 +66,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -65,20 +66,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, localFunctionClosureParameter: false);
}
if (context.Settings.LocalFunctions && f.Kind == ILFunctionKind.LocalFunction && v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index.Value] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p, decompilationContext)) {
AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body, localFunctionClosureParameter: true);
}
}
foreach (var displayClass in displayClasses.Values.OrderByDescending(d => d.Initializer.StartILOffset).ToArray()) {
@ -102,6 +94,59 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -102,6 +94,59 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
private void AddOrUpdateDisplayClass(ILFunction f, ILVariable v, ITypeDefinition closureType, ILInstruction inst, bool localFunctionClosureParameter)
{
var displayClass = displayClasses.Values.FirstOrDefault(c => c.Definition == 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");
if (displayClass == null) {
displayClasses.Add(v, new DisplayClass {
IsMono = isMono,
Initializer = inst,
Variable = v,
Definition = closureType,
Variables = new Dictionary<IField, DisplayClassVariable>(),
CaptureScope = (isMono && IsMonoNestedCaptureScope(closureType)) || localFunctionClosureParameter ? null : v.CaptureScope,
DeclaringFunction = localFunctionClosureParameter ? f.DeclarationScope.Ancestors.OfType<ILFunction>().First() : f
});
} else {
if (displayClass.CaptureScope == null && !localFunctionClosureParameter)
displayClass.CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : v.CaptureScope;
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 (context.Settings.LocalFunctions && closureType?.Kind == TypeKind.Struct && variable.HasInitialValue && IsPotentialClosure(this.context, closureType)) {
initializer = LocalFunctionDecompiler.GetStatement(variable.AddressInstructions.OrderBy(i => i.StartILOffset).First());
return true;
}
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);
@ -181,6 +226,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -181,6 +226,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())
@ -193,6 +244,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -193,6 +244,13 @@ 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;
return false;
}
protected override void Default(ILInstruction inst)
{
foreach (var child in inst.Children) {
@ -209,18 +267,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -209,18 +267,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (inst.Value.MatchLdLoc(out var closureVariable) && displayClasses.TryGetValue(closureVariable, out var displayClass)) {
displayClasses[inst.Variable] = displayClass;
instructionsToRemove.Add(inst);
} else if (inst.Variable.Kind == VariableKind.Local && inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 0 && inst.Value is StLoc) {
inst.ReplaceWith(inst.Value);
}
}
bool IsClosureInit(StLoc inst, out ITypeDefinition closureType)
{
closureType = null;
if (!(inst.Value is NewObj newObj))
return false;
closureType = newObj.Method.DeclaringTypeDefinition;
return closureType != null && IsPotentialClosure(this.context, newObj);
}
protected internal override void VisitStObj(StObj inst)
{
base.VisitStObj(inst);
@ -231,7 +282,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -231,7 +282,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)) {
@ -247,10 +298,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -247,10 +298,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
} else {
Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition);
// Introduce a fresh variable for the display class field.
v = currentFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name);
if (displayClass.IsMono && displayClass.CaptureScope == null && !IsOuterClosureReference(field)) {
displayClass.CaptureScope = BlockContainer.FindClosestContainer(inst);
}
v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name);
v.CaptureScope = displayClass.CaptureScope;
inst.ReplaceWith(new StLoc(v, inst.Value).WithILRange(inst));
value = new LdLoc(v);
@ -266,7 +317,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -266,7 +317,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))
@ -291,13 +342,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -291,13 +342,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (inst.Parent is LdObj || inst.Parent is StObj)
return;
// Get display class info
if (!(inst.Target is LdLoc displayClassLoad && displayClasses.TryGetValue(displayClassLoad.Variable, out var displayClass)))
if (!IsDisplayClassLoad(inst.Target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass))
return;
var field = (IField)inst.Field.MemberDefinition;
if (!displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) {
// Introduce a fresh variable for the display class field.
Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition);
var v = currentFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name);
var v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name);
v.CaptureScope = displayClass.CaptureScope;
inst.ReplaceWith(new LdLoca(v).WithILRange(inst));
displayClass.Variables.Add(field, new DisplayClassVariable { Value = new LdLoc(v), Variable = v });

9
ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs

@ -156,6 +156,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -156,6 +156,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var returnType = functionType.GetDelegateInvokeMethod()?.ReturnType;
var function = new ILFunction(returnType, parameterList, context.Function.GenericContext, container);
function.DelegateType = functionType;
function.Kind = IsExpressionTree(functionType) ? ILFunctionKind.ExpressionTree : ILFunctionKind.Delegate;
function.Variables.AddRange(parameterVariablesList);
function.AddILRange(instruction);
lambdaStack.Push(function);
@ -196,6 +197,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -196,6 +197,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
void SetExpressionTreeFlag(ILFunction lambda, CallInstruction call)
{
lambda.Kind = IsExpressionTree(call.Method.ReturnType) ? ILFunctionKind.ExpressionTree : ILFunctionKind.Delegate;
lambda.DelegateType = call.Method.ReturnType;
}
@ -340,8 +342,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -340,8 +342,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
return (null, SpecialType.UnknownType);
case ILFunction function:
if (function.IsExpressionTree) {
if (function.Kind == ILFunctionKind.ExpressionTree) {
function.DelegateType = UnwrapExpressionTree(function.DelegateType);
function.Kind = ILFunctionKind.Delegate;
}
return (function, function.DelegateType);
case LdLoc ldloc:
@ -371,6 +374,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -371,6 +374,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
bool IsExpressionTree(IType delegateType) => delegateType is ParameterizedType pt
&& pt.FullName == "System.Linq.Expressions.Expression"
&& pt.TypeArguments.Count == 1;
IType UnwrapExpressionTree(IType delegateType)
{
if (delegateType is ParameterizedType pt && pt.FullName == "System.Linq.Expressions.Expression" && pt.TypeArguments.Count == 1) {

11
ICSharpCode.Decompiler/Output/TextTokenWriter.cs

@ -21,8 +21,10 @@ using System.Collections.Generic; @@ -21,8 +21,10 @@ 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.Semantics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler
@ -169,13 +171,18 @@ namespace ICSharpCode.Decompiler @@ -169,13 +171,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 MemberResolveResult;
if (localFunction != null)
return localFunction.Member;
}
return null;
}

6
ICSharpCode.Decompiler/TypeSystem/IMethod.cs

@ -53,6 +53,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -53,6 +53,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
IReadOnlyList<IType> TypeArguments { get; }
bool IsExtensionMethod { get; }
bool IsLocalFunction { get; }
bool IsConstructor { get; }
bool IsDestructor { get; }
bool IsOperator { get; }
@ -81,8 +82,9 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -81,8 +82,9 @@ namespace ICSharpCode.Decompiler.TypeSystem
MethodSemanticsAttributes AccessorKind { get; }
/// <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.
/// If this method is reduced from an extension method or a local function returns the original method, <c>null</c> otherwise.
/// A reduced method doesn't contain the extension method parameter. That means that it has one parameter less than its definition.
/// A local function doesn't contain compiler-generated method parameters at the end.
/// </summary>
IMethod ReducedFrom { get; }

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

@ -139,6 +139,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -139,6 +139,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
IReadOnlyList<IType> IMethod.TypeArguments => TypeParameters;
bool IMethod.IsExtensionMethod => false;
bool IMethod.IsLocalFunction => false;
bool IMethod.IsConstructor => symbolKind == SymbolKind.Constructor;
bool IMethod.IsDestructor => symbolKind == SymbolKind.Destructor;
bool IMethod.IsOperator => symbolKind == SymbolKind.Operator;

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

@ -0,0 +1,138 @@ @@ -0,0 +1,138 @@
// 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.Reflection;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{
/// <summary>
/// A local function has zero or more compiler-generated parameters added at the end.
/// </summary>
class LocalFunctionMethod : IMethod
{
readonly IMethod baseMethod;
public LocalFunctionMethod(IMethod baseMethod, int numberOfCompilerGeneratedParameters)
{
this.baseMethod = baseMethod;
this.NumberOfCompilerGeneratedParameters = numberOfCompilerGeneratedParameters;
}
public bool Equals(IMember obj, TypeVisitor typeNormalization)
{
if (!(obj is LocalFunctionMethod other))
return false;
return baseMethod.Equals(other.baseMethod, typeNormalization)
&& NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters;
}
public override bool Equals(object obj)
{
if (!(obj is LocalFunctionMethod other))
return false;
return baseMethod.Equals(other.baseMethod)
&& NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters;
}
public override int GetHashCode()
{
unchecked {
return baseMethod.GetHashCode() + NumberOfCompilerGeneratedParameters + 1;
}
}
public override string ToString()
{
return string.Format("[LocalFunctionMethod: ReducedFrom={0}, NumberOfGeneratedParameters={1}]", ReducedFrom, NumberOfCompilerGeneratedParameters);
}
internal int NumberOfCompilerGeneratedParameters { get; }
public IMember MemberDefinition => this;
public IType ReturnType => baseMethod.ReturnType;
IEnumerable<IMember> IMember.ExplicitlyImplementedInterfaceMembers => baseMethod.ExplicitlyImplementedInterfaceMembers;
bool IMember.IsExplicitInterfaceImplementation => baseMethod.IsExplicitInterfaceImplementation;
public bool IsVirtual => baseMethod.IsVirtual;
public bool IsOverride => baseMethod.IsOverride;
public bool IsOverridable => baseMethod.IsOverridable;
public TypeParameterSubstitution Substitution => baseMethod.Substitution;
public IMethod Specialize(TypeParameterSubstitution substitution)
{
return SpecializedMethod.Create(this, substitution);
}
IMember IMember.Specialize(TypeParameterSubstitution substitution)
{
return Specialize(substitution);
}
public IReadOnlyList<ITypeParameter> TypeParameters => baseMethod.TypeParameters;
public bool IsExtensionMethod => baseMethod.IsExtensionMethod;
public bool IsLocalFunction => true;
public bool IsConstructor => baseMethod.IsConstructor;
public bool IsDestructor => baseMethod.IsDestructor;
public bool IsOperator => baseMethod.IsOperator;
public bool HasBody => baseMethod.HasBody;
public bool IsAccessor => baseMethod.IsAccessor;
public IMember AccessorOwner => baseMethod.AccessorOwner;
public MethodSemanticsAttributes AccessorKind => baseMethod.AccessorKind;
public IMethod ReducedFrom => baseMethod;
public IReadOnlyList<IType> TypeArguments => baseMethod.TypeArguments;
List<IParameter> parameters;
public IReadOnlyList<IParameter> Parameters {
get {
if (parameters == null)
parameters = new List<IParameter>(baseMethod.Parameters.SkipLast(NumberOfCompilerGeneratedParameters));
return parameters;
}
}
public System.Reflection.Metadata.EntityHandle MetadataToken => baseMethod.MetadataToken;
public SymbolKind SymbolKind => baseMethod.SymbolKind;
public ITypeDefinition DeclaringTypeDefinition => baseMethod.DeclaringTypeDefinition;
public IType DeclaringType => baseMethod.DeclaringType;
public IModule ParentModule => baseMethod.ParentModule;
IEnumerable<IAttribute> IEntity.GetAttributes() => baseMethod.GetAttributes();
IEnumerable<IAttribute> IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes();
bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly;
/// <summary>
/// We consider local functions as always static, because they do not have a "this parameter".
/// Even local functions in instance methods capture this.
/// </summary>
public bool IsStatic => true;
public bool IsAbstract => baseMethod.IsAbstract;
public bool IsSealed => baseMethod.IsSealed;
public Accessibility Accessibility => baseMethod.Accessibility;
public string FullName => baseMethod.FullName;
public string Name => baseMethod.Name;
public string ReflectionName => baseMethod.ReflectionName;
public string Namespace => baseMethod.Namespace;
public ICompilation Compilation => baseMethod.Compilation;
}
}

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

@ -41,6 +41,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -41,6 +41,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
readonly EntityHandle accessorOwner;
public MethodSemanticsAttributes AccessorKind { get; }
public bool IsExtensionMethod { get; }
bool IMethod.IsLocalFunction => false;
// lazy-loaded fields:
ITypeDefinition declaringType;

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

@ -102,11 +102,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -102,11 +102,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
return specializedTypeParameters ?? methodDefinition.TypeParameters;
}
}
public bool IsExtensionMethod {
get { return methodDefinition.IsExtensionMethod; }
}
public bool IsLocalFunction {
get { return methodDefinition.IsLocalFunction; }
}
public bool IsConstructor {
get { return methodDefinition.IsConstructor; }
}

4
ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs

@ -129,6 +129,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -129,6 +129,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
get { return baseMethod.IsExtensionMethod; }
}
bool IMethod.IsLocalFunction {
get { return baseMethod.IsLocalFunction; }
}
public bool IsConstructor {
get { return baseMethod.IsConstructor; }
}

2
ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs

@ -94,7 +94,7 @@ namespace ILSpy.BamlDecompiler.Rewrite @@ -94,7 +94,7 @@ namespace ILSpy.BamlDecompiler.Rewrite
// decompile method and optimize the switch
var ilReader = new ILReader(ctx.TypeSystem.MainModule);
var function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, body, genericContext, ctx.CancellationToken);
var function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, body, genericContext, ILFunctionKind.TopLevelFunction, ctx.CancellationToken);
var context = new ILTransformContext(function, ctx.TypeSystem, null) {
CancellationToken = ctx.CancellationToken

2
ILSpy/Languages/ILAstLanguage.cs

@ -119,7 +119,7 @@ namespace ICSharpCode.ILSpy @@ -119,7 +119,7 @@ namespace ICSharpCode.ILSpy
var reader = new ILReader(typeSystem.MainModule);
reader.UseDebugSymbols = options.DecompilerSettings.UseDebugSymbols;
var methodBody = module.Reader.GetMethodBody(methodDef.RelativeVirtualAddress);
ILFunction il = reader.ReadIL((SRM.MethodDefinitionHandle)method.MetadataToken, methodBody, cancellationToken: options.CancellationToken);
ILFunction il = reader.ReadIL((SRM.MethodDefinitionHandle)method.MetadataToken, methodBody, kind: ILFunctionKind.TopLevelFunction, cancellationToken: options.CancellationToken);
var namespaces = new HashSet<string>();
var decompiler = new CSharpDecompiler(typeSystem, options.DecompilerSettings) { CancellationToken = options.CancellationToken };
ILTransformContext context = decompiler.CreateILTransformContext(il);

6
ILSpy/Properties/Resources.Designer.cs generated

@ -700,11 +700,11 @@ namespace ICSharpCode.ILSpy.Properties { @@ -700,11 +700,11 @@ namespace ICSharpCode.ILSpy.Properties {
}
/// <summary>
/// Looks up a localized string similar to Introduce local functions (NOT IMPLEMENTED!).
/// Looks up a localized string similar to Introduce local functions.
/// </summary>
public static string DecompilerSettings_IntroduceLocalFunctionsNOTIMPLEMENTED {
public static string DecompilerSettings_IntroduceLocalFunctions {
get {
return ResourceManager.GetString("DecompilerSettings.IntroduceLocalFunctionsNOTIMPLEMENTED", resourceCulture);
return ResourceManager.GetString("DecompilerSettings.IntroduceLocalFunctions", resourceCulture);
}
}

4
ILSpy/Properties/Resources.resx

@ -702,8 +702,8 @@ @@ -702,8 +702,8 @@
<data name="DecompilerSettings.RemoveOptionalArgumentsIfPossible" xml:space="preserve">
<value>Remove optional arguments, if possible</value>
</data>
<data name="DecompilerSettings.IntroduceLocalFunctionsNOTIMPLEMENTED" xml:space="preserve">
<value>Introduce local functions (NOT IMPLEMENTED!)</value>
<data name="DecompilerSettings.IntroduceLocalFunctions" xml:space="preserve">
<value>Introduce local functions</value>
</data>
<data name="DecompilerSettings.NullableReferenceTypes" xml:space="preserve">
<value>Nullable reference types</value>

6
ILSpy/Properties/Resources.zh-Hans.resx

@ -676,7 +676,7 @@ @@ -676,7 +676,7 @@
<value>IsByRefLikeAttribute应替换为结构上的 "ref" 修饰符</value>
</data>
<data name="DecompilerSettings.IsReadOnlyAttributeShouldBeReplacedWithReadonlyInModifiersOnStructsParameters" xml:space="preserve">
<value>IsReadOnlyAttribute 应替为结构参数上的 "readonly"/"中的修饰符</value>
<value>IsReadOnlyAttribute 应替为结构参数上的 "readonly"/"in"中的修饰符</value>
</data>
<data name="DecompilerSettings.IsUnmanagedAttributeOnTypeParametersShouldBeReplacedWithUnmanagedConstraints" xml:space="preserve">
<value>类型参数上的IsUnmanagedAttribute 应替换为 "非托管" 约束</value>
@ -702,8 +702,8 @@ @@ -702,8 +702,8 @@
<data name="DecompilerSettings.RemoveOptionalArgumentsIfPossible" xml:space="preserve">
<value>如果可能, 删除可选参数</value>
</data>
<data name="DecompilerSettings.IntroduceLocalFunctionsNOTIMPLEMENTED" xml:space="preserve">
<value>引入本地功能 (未实现!)</value>
<data name="DecompilerSettings.IntroduceLocalFunctions" xml:space="preserve">
<value>引入本地功能 </value>
</data>
<data name="DecompilerSettings.C70LocalFunctionsAreNotImplemented" xml:space="preserve">
<value>C# 7.0 本地函数未实现!</value>

Loading…
Cancel
Save