Browse Source

Add detection of local functions, so we do not hide the methods/display classes.

pull/1274/merge
Siegfried Pammer 7 years ago
parent
commit
cf1d05042f
  1. 6
      ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs
  2. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  3. 76
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/LocalFunctions.cs
  4. 14
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  5. 20
      ICSharpCode.Decompiler/DecompilerSettings.cs
  6. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  7. 113
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs

6
ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs

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

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -68,6 +68,7 @@
<Compile Include="Semantics\ExplicitConversionTest.cs" /> <Compile Include="Semantics\ExplicitConversionTest.cs" />
<Compile Include="Semantics\OverloadResolutionTests.cs" /> <Compile Include="Semantics\OverloadResolutionTests.cs" />
<Compile Include="DataFlowTest.cs" /> <Compile Include="DataFlowTest.cs" />
<Compile Include="TestCases\Correctness\LocalFunctions.cs" />
<Compile Include="TestCases\Correctness\RefLocalsAndReturns.cs" /> <Compile Include="TestCases\Correctness\RefLocalsAndReturns.cs" />
<Compile Include="TestCases\Pretty\OptionalArguments.cs" /> <Compile Include="TestCases\Pretty\OptionalArguments.cs" />
<Compile Include="TestCases\Pretty\CustomShortCircuitOperators.cs" /> <Compile Include="TestCases\Pretty\CustomShortCircuitOperators.cs" />

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

@ -0,0 +1,76 @@
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();
}
}
}
}

14
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -249,9 +249,11 @@ namespace ICSharpCode.Decompiler.CSharp
var methodSemantics = module.MethodSemanticsLookup.GetSemantics(methodHandle).Item2; var methodSemantics = module.MethodSemanticsLookup.GetSemantics(methodHandle).Item2;
if (methodSemantics != 0 && methodSemantics != System.Reflection.MethodSemanticsAttributes.Other) if (methodSemantics != 0 && methodSemantics != System.Reflection.MethodSemanticsAttributes.Other)
return true; return true;
if (LocalFunctionDecompiler.IsLocalFunctionMethod(module, methodHandle))
return settings.LocalFunctions;
if (settings.AnonymousMethods && methodHandle.HasGeneratedName(metadata) && methodHandle.IsCompilerGenerated(metadata)) if (settings.AnonymousMethods && methodHandle.HasGeneratedName(metadata) && methodHandle.IsCompilerGenerated(metadata))
return true; return true;
if (settings.AsyncAwait && AsyncAwaitDecompiler.IsCompilerGeneratedMainMethod(module, (MethodDefinitionHandle)member)) if (settings.AsyncAwait && AsyncAwaitDecompiler.IsCompilerGeneratedMainMethod(module, methodHandle))
return true; return true;
return false; return false;
case HandleKind.TypeDefinition: case HandleKind.TypeDefinition:
@ -259,6 +261,8 @@ namespace ICSharpCode.Decompiler.CSharp
var type = metadata.GetTypeDefinition(typeHandle); var type = metadata.GetTypeDefinition(typeHandle);
name = metadata.GetString(type.Name); name = metadata.GetString(type.Name);
if (!type.GetDeclaringType().IsNil) { if (!type.GetDeclaringType().IsNil) {
if (LocalFunctionDecompiler.IsLocalFunctionDisplayClass(module, typeHandle))
return settings.LocalFunctions;
if (settings.AnonymousMethods && IsClosureType(type, metadata)) if (settings.AnonymousMethods && IsClosureType(type, metadata))
return true; return true;
if (settings.YieldReturn && YieldReturnDecompiler.IsCompilerGeneratorEnumerator(typeHandle, metadata)) if (settings.YieldReturn && YieldReturnDecompiler.IsCompilerGeneratorEnumerator(typeHandle, metadata))
@ -1070,6 +1074,14 @@ namespace ICSharpCode.Decompiler.CSharp
} }
FixParameterNames(methodDecl); FixParameterNames(methodDecl);
var methodDefinition = metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken); var methodDefinition = metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken);
if (!settings.LocalFunctions && LocalFunctionDecompiler.IsLocalFunctionMethod(method.ParentModule.PEFile, (MethodDefinitionHandle)method.MetadataToken)) {
// if local functions are not active and we're dealing with a local function,
// reduce the visibility of the method to private,
// otherwise this leads to compile errors because the display classes have lesser accessibility.
// Note: removing and then adding the static modifier again is necessary to set the private modifier before all other modifiers.
methodDecl.Modifiers &= ~(Modifiers.Internal | Modifiers.Static);
methodDecl.Modifiers |= Modifiers.Private | (method.IsStatic ? Modifiers.Static : 0);
}
if (methodDefinition.HasBody()) { if (methodDefinition.HasBody()) {
DecompileBody(method, methodDecl, decompileRun, decompilationContext); DecompileBody(method, methodDecl, decompileRun, decompilationContext);
} else if (!method.IsAbstract && method.DeclaringType.Kind != TypeKind.Interface) { } else if (!method.IsAbstract && method.DeclaringType.Kind != TypeKind.Interface) {

20
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -81,6 +81,7 @@ namespace ICSharpCode.Decompiler
tupleTypes = false; tupleTypes = false;
tupleConversions = false; tupleConversions = false;
discards = false; discards = false;
localFunctions = false;
} }
if (languageVersion < CSharp.LanguageVersion.CSharp7_2) { if (languageVersion < CSharp.LanguageVersion.CSharp7_2) {
introduceReadonlyAndInModifiers = false; introduceReadonlyAndInModifiers = false;
@ -100,7 +101,7 @@ namespace ICSharpCode.Decompiler
if (introduceRefModifiersOnStructs || introduceReadonlyAndInModifiers || nonTrailingNamedArguments) if (introduceRefModifiersOnStructs || introduceReadonlyAndInModifiers || nonTrailingNamedArguments)
return CSharp.LanguageVersion.CSharp7_2; return CSharp.LanguageVersion.CSharp7_2;
// C# 7.1 missing // C# 7.1 missing
if (outVariables || tupleTypes || tupleConversions || discards) if (outVariables || tupleTypes || tupleConversions || discards || localFunctions)
return CSharp.LanguageVersion.CSharp7; return CSharp.LanguageVersion.CSharp7;
if (awaitInCatchFinally || useExpressionBodyForCalculatedGetterOnlyProperties || nullPropagation if (awaitInCatchFinally || useExpressionBodyForCalculatedGetterOnlyProperties || nullPropagation
|| stringInterpolation || dictionaryInitializers || extensionMethodsInCollectionInitializers) || stringInterpolation || dictionaryInitializers || extensionMethodsInCollectionInitializers)
@ -774,6 +775,23 @@ namespace ICSharpCode.Decompiler
} }
} }
bool localFunctions = false;
/// <summary>
/// Gets/Sets whether C# 7.0 local functions should be used.
/// Note: this language feature is currenly not implemented and this setting is always false.
/// </summary>
public bool LocalFunctions {
get { return localFunctions; }
set {
if (localFunctions != value) {
throw new NotImplementedException("C# 7.0 local functions are not implemented!");
//localFunctions = value;
//OnPropertyChanged();
}
}
}
#region Options to aid VB decompilation #region Options to aid VB decompilation
bool assumeArrayLengthFitsIntoInt32 = true; bool assumeArrayLengthFitsIntoInt32 = true;

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -267,6 +267,7 @@
<Compile Include="CSharp\Transforms\AddXmlDocumentationTransform.cs" /> <Compile Include="CSharp\Transforms\AddXmlDocumentationTransform.cs" />
<Compile Include="DecompileRun.cs" /> <Compile Include="DecompileRun.cs" />
<Compile Include="Disassembler\ILParser.cs" /> <Compile Include="Disassembler\ILParser.cs" />
<Compile Include="IL\Transforms\LocalFunctionDecompiler.cs" />
<Compile Include="IL\Transforms\UserDefinedLogicTransform.cs" /> <Compile Include="IL\Transforms\UserDefinedLogicTransform.cs" />
<Compile Include="Metadata\AssemblyReferences.cs" /> <Compile Include="Metadata\AssemblyReferences.cs" />
<Compile Include="Metadata\CodeMappingInfo.cs" /> <Compile Include="Metadata\CodeMappingInfo.cs" />

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

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Reflection;
using System.Reflection.Metadata;
using System.Text;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL.Transforms
{
class LocalFunctionDecompiler : IILTransform
{
public void Run(ILFunction function, ILTransformContext context)
{
throw new NotImplementedException();
}
public static bool IsLocalFunctionMethod(PEFile module, MethodDefinitionHandle methodHandle)
{
var metadata = module.Metadata;
var method = metadata.GetMethodDefinition(methodHandle);
if ((method.Attributes & MethodAttributes.Assembly) == 0 || !method.IsCompilerGenerated(metadata))
return false;
if (!ParseLocalFunctionName(metadata.GetString(method.Name), out _, out _))
return false;
return true;
}
public static bool IsLocalFunctionDisplayClass(PEFile module, TypeDefinitionHandle typeHandle)
{
var metadata = module.Metadata;
var type = metadata.GetTypeDefinition(typeHandle);
if ((type.Attributes & TypeAttributes.NestedPrivate) == 0)
return false;
if (!type.HasGeneratedName(metadata))
return false;
var declaringTypeHandle = type.GetDeclaringType();
var declaringType = metadata.GetTypeDefinition(declaringTypeHandle);
foreach (var method in declaringType.GetMethods()) {
if (!IsLocalFunctionMethod(module, method))
continue;
var md = metadata.GetMethodDefinition(method);
if (md.DecodeSignature(new FindTypeDecoder(typeHandle), default).ParameterTypes.Any())
return true;
}
return false;
}
/// <summary>
/// Newer Roslyn versions use the format "&ltcallerName&gtg__functionName|x_y"
/// Older versions use "&ltcallerName&gtg__functionNamex_y"
/// </summary>
static readonly Regex functionNameRegex = new Regex(@"^<(.*)>g__(.*)\|{0,1}\d+_\d+$", RegexOptions.Compiled);
static bool ParseLocalFunctionName(string name, out string callerName, out string functionName)
{
callerName = null;
functionName = null;
if (string.IsNullOrWhiteSpace(name))
return false;
var match = functionNameRegex.Match(name);
callerName = match.Groups[1].Value;
functionName = match.Groups[2].Value;
return match.Success;
}
struct FindTypeDecoder : ISignatureTypeProvider<bool, Unit>
{
TypeDefinitionHandle handle;
public FindTypeDecoder(TypeDefinitionHandle handle)
{
this.handle = handle;
}
public bool GetArrayType(bool elementType, ArrayShape shape) => elementType;
public bool GetByReferenceType(bool elementType) => elementType;
public bool GetFunctionPointerType(MethodSignature<bool> signature) => false;
public bool GetGenericInstantiation(bool genericType, ImmutableArray<bool> typeArguments) => genericType;
public bool GetGenericMethodParameter(Unit genericContext, int index) => false;
public bool GetGenericTypeParameter(Unit genericContext, int index) => false;
public bool GetModifiedType(bool modifier, bool unmodifiedType, bool isRequired) => unmodifiedType;
public bool GetPinnedType(bool elementType) => elementType;
public bool GetPointerType(bool elementType) => elementType;
public bool GetPrimitiveType(PrimitiveTypeCode typeCode) => false;
public bool GetSZArrayType(bool elementType) => false;
public bool GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind)
{
return this.handle == handle;
}
public bool GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind)
{
return false;
}
public bool GetTypeFromSpecification(MetadataReader reader, Unit genericContext, TypeSpecificationHandle handle, byte rawTypeKind)
{
return reader.GetTypeSpecification(handle).DecodeSignature(this, genericContext);
}
}
}
}
Loading…
Cancel
Save