Browse Source

Fix #1682, fix #1949, fix #2394 and fix #3684: Added basic algorithm of IsAccessorInterfaceImplementationRuntimeHelper

pull/3686/head
Siegfried Pammer 5 years ago
parent
commit
faeae3f083
  1. 1
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3684.cs
  2. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3684.dep.cs
  3. 161
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  4. 137
      ICSharpCode.Decompiler/Metadata/SignatureBlobComparer.cs

1
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3684.cs

@ -8,6 +8,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -8,6 +8,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public interface IInterface
{
string Name { get; set; }
T Convert<T>(T input);
}
public class DerivedClass : BaseClass, IInterface

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3684.dep.cs

@ -12,5 +12,7 @@ namespace CrossAssemblyDep @@ -12,5 +12,7 @@ namespace CrossAssemblyDep
name = value;
}
}
public T Convert<T>(T input) { return input; }
}
}

161
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -292,6 +292,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -292,6 +292,8 @@ namespace ICSharpCode.Decompiler.CSharp
name = metadata.GetString(method.Name);
if (name == ".ctor" && method.RelativeVirtualAddress == 0 && metadata.GetTypeDefinition(method.GetDeclaringType()).Attributes.HasFlag(System.Reflection.TypeAttributes.Import))
return true;
if (module is PEFile m && IsAccessorInterfaceImplementationRuntimeHelper(m, methodHandle))
return true;
if (settings.LocalFunctions && LocalFunctionDecompiler.IsLocalFunctionMethod(module, methodHandle))
return true;
if (settings.AnonymousMethods && methodHandle.HasGeneratedName(metadata) && methodHandle.IsCompilerGenerated(metadata))
@ -386,6 +388,165 @@ namespace ICSharpCode.Decompiler.CSharp @@ -386,6 +388,165 @@ namespace ICSharpCode.Decompiler.CSharp
return name.StartsWith("<", StringComparison.Ordinal) && name.EndsWith(">P", StringComparison.Ordinal);
}
static bool IsAccessorInterfaceImplementationRuntimeHelper(PEFile module, MethodDefinitionHandle handle)
{
var metadata = module.Metadata;
var method = metadata.GetMethodDefinition(handle);
if ((method.Attributes & System.Reflection.MethodAttributes.Static) != 0)
return false;
string rawName = metadata.GetString(method.Name);
int dot = rawName.LastIndexOf('.');
if (dot < 0)
return false;
string name = rawName.Substring(dot + 1);
if (handle.GetMethodImplementations(metadata).Length == 0)
return false;
if (method.RelativeVirtualAddress == 0)
return false;
if (!name.StartsWith("get_", StringComparison.Ordinal) &&
!name.StartsWith("set_", StringComparison.Ordinal) &&
!name.StartsWith("add_", StringComparison.Ordinal) &&
!name.StartsWith("remove_", StringComparison.Ordinal) &&
!name.StartsWith("raise_", StringComparison.Ordinal))
{
return false;
}
var signature = metadata.GetBlobReader(method.Signature);
(int genericParameterCount, int parameterCount) = SignatureBlobComparer.ReadParameterCount(ref signature);
if (genericParameterCount == -1 || parameterCount == -1)
return false;
signature.Reset();
int maximumMethodSize = 4 * (parameterCount + 1) + 5 + 1;
var body = module.Reader.GetMethodBody(method.RelativeVirtualAddress);
var reader = body.GetILReader();
if (reader.RemainingBytes > maximumMethodSize)
return false;
for (int i = 0; i < parameterCount + 1; i++)
{
int index;
switch (reader.DecodeOpCode())
{
case ILOpCode.Ldarg:
index = reader.ReadUInt16();
if (index != i)
return false;
break;
case ILOpCode.Ldarg_s:
index = reader.ReadByte();
if (index != i)
return false;
break;
case ILOpCode.Ldarg_0:
if (i != 0)
return false;
break;
case ILOpCode.Ldarg_1:
if (i != 1)
return false;
break;
case ILOpCode.Ldarg_2:
if (i != 2)
return false;
break;
case ILOpCode.Ldarg_3:
if (i != 3)
return false;
break;
default:
return false;
}
}
if (reader.DecodeOpCode() != ILOpCode.Call)
return false;
EntityHandle targetHandle = MetadataTokenHelpers.EntityHandleOrNil(reader.ReadInt32());
if (targetHandle.IsNil)
return false;
if (reader.DecodeOpCode() != ILOpCode.Ret)
return false;
if (reader.RemainingBytes != 0)
return false;
BlobReader signature2;
string otherName;
switch (targetHandle.Kind)
{
case HandleKind.MethodDefinition:
if (genericParameterCount != 0)
return false;
var methodDef = metadata.GetMethodDefinition((MethodDefinitionHandle)targetHandle);
signature2 = metadata.GetBlobReader(methodDef.Signature);
otherName = metadata.GetString(methodDef.Name);
break;
case HandleKind.MethodSpecification:
if (genericParameterCount == 0)
return false;
var methodSpec = metadata.GetMethodSpecification((MethodSpecificationHandle)targetHandle);
var instantiationBlob = metadata.GetBlobReader(methodSpec.Signature);
if (!IsIdentityInstantiation(ref instantiationBlob, genericParameterCount))
return false;
switch (methodSpec.Method.Kind)
{
case HandleKind.MethodDefinition:
var methodSpecDef = metadata.GetMethodDefinition((MethodDefinitionHandle)methodSpec.Method);
signature2 = metadata.GetBlobReader(methodSpecDef.Signature);
otherName = metadata.GetString(methodSpecDef.Name);
break;
case HandleKind.MemberReference:
var methodSpecRef = metadata.GetMemberReference((MemberReferenceHandle)methodSpec.Method);
if (methodSpecRef.GetKind() != MemberReferenceKind.Method)
return false;
signature2 = metadata.GetBlobReader(methodSpecRef.Signature);
otherName = metadata.GetString(methodSpecRef.Name);
break;
default:
return false;
}
break;
case HandleKind.MemberReference:
if (genericParameterCount != 0)
return false;
var methodRef = metadata.GetMemberReference((MemberReferenceHandle)targetHandle);
if (methodRef.GetKind() != MemberReferenceKind.Method)
return false;
signature2 = metadata.GetBlobReader(methodRef.Signature);
otherName = metadata.GetString(methodRef.Name);
break;
default:
return false;
}
if (otherName != name)
return false;
return SignatureBlobComparer.EqualsMethodSignature(signature, signature2, metadata, metadata, skipModifiers: true);
static bool IsIdentityInstantiation(ref BlobReader reader, int expectedCount)
{
// Format: GENRICINST count type1 type2 ...
if (reader.ReadByte() != 0x0A) // GENERICINST
return false;
if (!reader.TryReadCompressedInteger(out int count) || count != expectedCount)
return false;
for (int i = 0; i < count; i++)
{
if (reader.ReadByte() != 0x1E) // ELEMENT_TYPE_MVAR
return false;
if (!reader.TryReadCompressedInteger(out int index) || index != i)
return false;
}
return true;
}
}
static bool IsSwitchOnStringCache(SRM.FieldDefinition field, MetadataReader metadata)
{
return metadata.GetString(field.Name).StartsWith("<>f__switch", StringComparison.Ordinal);

137
ICSharpCode.Decompiler/Metadata/SignatureBlobComparer.cs

@ -16,18 +16,74 @@ @@ -16,18 +16,74 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.Diagnostics;
using System.Reflection.Metadata;
namespace ICSharpCode.Decompiler.Metadata
{
public static class SignatureBlobComparer
{
public static bool EqualsMethodSignature(BlobReader a, BlobReader b, MetadataReader contextForA, MetadataReader contextForB)
internal static (int GenericParameterCount, int ParameterCount) ReadParameterCount(ref BlobReader reader)
{
return EqualsMethodSignature(ref a, ref b, contextForA, contextForB);
if (reader.RemainingBytes == 0)
return (-1, -1);
var header = reader.ReadSignatureHeader();
int gp;
if (header.IsGeneric)
{
if (!reader.TryReadCompressedInteger(out gp))
gp = -1;
}
else
{
gp = 0;
}
if (!reader.TryReadCompressedInteger(out int p))
p = -1;
return (gp, p);
}
public static bool EqualsMethodSignature(BlobReader a, BlobReader b, MetadataReader contextForA, MetadataReader contextForB, bool skipModifiers = false)
{
return EqualsMethodSignature(ref a, ref b, contextForA, contextForB, skipModifiers);
}
static int SkipCustomModifiers(ref BlobReader reader, int typeCode)
{
while (typeCode is 0x1F or 0x20) // ELEMENT_TYPE_CMOD_REQD or ELEMENT_TYPE_CMOD_OPT
{
reader.ReadTypeHandle(); // discard modifier
if (!reader.TryReadCompressedInteger(out typeCode))
return -1;
}
return typeCode;
}
static bool TypeSlotEquals(ref BlobReader a, ref BlobReader b, MetadataReader contextForA, MetadataReader contextForB, bool skipModifiers)
{
if (!skipModifiers)
{
if (!IsSameCompressedInteger(ref a, ref b, out int typeCode))
return false;
return TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode, false);
}
if (!a.TryReadCompressedInteger(out int typeCodeA) || !b.TryReadCompressedInteger(out int typeCodeB))
return false;
typeCodeA = SkipCustomModifiers(ref a, typeCodeA);
typeCodeB = SkipCustomModifiers(ref b, typeCodeB);
if (typeCodeA < 0 || typeCodeB < 0)
return false;
if (typeCodeA != typeCodeB)
return false;
return TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCodeA, true);
}
static bool EqualsMethodSignature(ref BlobReader a, ref BlobReader b, MetadataReader contextForA, MetadataReader contextForB)
static bool EqualsMethodSignature(ref BlobReader a, ref BlobReader b,
MetadataReader contextForA, MetadataReader contextForB, bool skipModifiers)
{
SignatureHeader header;
// compare signature headers
@ -42,26 +98,37 @@ namespace ICSharpCode.Decompiler.Metadata @@ -42,26 +98,37 @@ namespace ICSharpCode.Decompiler.Metadata
// read & compare parameter count
if (!IsSameCompressedInteger(ref a, ref b, out int totalParameterCount))
return false;
if (!IsSameCompressedInteger(ref a, ref b, out int typeCode))
return false;
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode))
// return type
if (!TypeSlotEquals(ref a, ref b, contextForA, contextForB, skipModifiers))
return false;
int i = 0;
for (; i < totalParameterCount; i++)
{
if (!IsSameCompressedInteger(ref a, ref b, out typeCode))
// peek at both sides for varargs sentinel or type code
if (!a.TryReadCompressedInteger(out int typeCodeA) || !b.TryReadCompressedInteger(out int typeCodeB))
return false;
// varargs sentinel
if (typeCode == 65)
if (typeCodeA == 65 || typeCodeB == 65)
{
if (typeCodeA != typeCodeB)
return false;
break;
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode))
}
if (skipModifiers)
{
typeCodeA = SkipCustomModifiers(ref a, typeCodeA);
typeCodeB = SkipCustomModifiers(ref b, typeCodeB);
if (typeCodeA < 0 || typeCodeB < 0)
return false;
}
if (typeCodeA != typeCodeB)
return false;
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCodeA, skipModifiers))
return false;
}
for (; i < totalParameterCount; i++)
{
if (!IsSameCompressedInteger(ref a, ref b, out typeCode))
return false;
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode))
if (!TypeSlotEquals(ref a, ref b, contextForA, contextForB, skipModifiers))
return false;
}
return true;
@ -76,7 +143,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -76,7 +143,7 @@ namespace ICSharpCode.Decompiler.Metadata
{
if (!IsSameCompressedInteger(ref a, ref b, out int typeCode))
return false;
return TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode);
return TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode, false);
}
static bool IsSameCompressedInteger(ref BlobReader a, ref BlobReader b, out int value)
@ -89,14 +156,15 @@ namespace ICSharpCode.Decompiler.Metadata @@ -89,14 +156,15 @@ namespace ICSharpCode.Decompiler.Metadata
return a.TryReadCompressedSignedInteger(out value) && b.TryReadCompressedSignedInteger(out int otherValue) && value == otherValue;
}
static bool TypesAreEqual(ref BlobReader a, ref BlobReader b, MetadataReader contextForA, MetadataReader contextForB, int typeCode)
static bool TypesAreEqual(ref BlobReader a, ref BlobReader b, MetadataReader contextForA, MetadataReader contextForB, int typeCode, bool skipModifiers)
{
Debug.Assert(typeCode != 0x1F && typeCode != 0x20, "Unexpected ELEMENT_TYPE_CMOD_REQD or ELEMENT_TYPE_CMOD_OPT");
switch (typeCode)
{
case 0x1: // ELEMENT_TYPE_VOID
case 0x2: // ELEMENT_TYPE_BOOLEAN
case 0x3: // ELEMENT_TYPE_CHAR
case 0x4: // ELEMENT_TYPE_I1
case 0x2: // ELEMENT_TYPE_BOOLEAN
case 0x3: // ELEMENT_TYPE_CHAR
case 0x4: // ELEMENT_TYPE_I1
case 0x5: // ELEMENT_TYPE_U1
case 0x6: // ELEMENT_TYPE_I2
case 0x7: // ELEMENT_TYPE_U2
@ -112,24 +180,24 @@ namespace ICSharpCode.Decompiler.Metadata @@ -112,24 +180,24 @@ namespace ICSharpCode.Decompiler.Metadata
case 0x19: // ELEMENT_TYPE_U
case 0x1C: // ELEMENT_TYPE_OBJECT
return true;
case 0xF: // ELEMENT_TYPE_PTR
case 0x10: // ELEMENT_TYPE_BYREF
case 0xF: // ELEMENT_TYPE_PTR
case 0x10: // ELEMENT_TYPE_BYREF
case 0x45: // ELEMENT_TYPE_PINNED
case 0x1D: // ELEMENT_TYPE_SZARRAY
if (!IsSameCompressedInteger(ref a, ref b, out typeCode))
return false;
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode))
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode, skipModifiers))
return false;
return true;
case 0x1B: // ELEMENT_TYPE_FNPTR
if (!EqualsMethodSignature(ref a, ref b, contextForA, contextForB))
case 0x1B: // ELEMENT_TYPE_FNPTR
if (!EqualsMethodSignature(ref a, ref b, contextForA, contextForB, skipModifiers))
return false;
return true;
case 0x14: // ELEMENT_TYPE_ARRAY
case 0x14: // ELEMENT_TYPE_ARRAY
// element type
if (!IsSameCompressedInteger(ref a, ref b, out typeCode))
return false;
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode))
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode, skipModifiers))
return false;
// rank
if (!IsSameCompressedInteger(ref a, ref b, out _))
@ -151,22 +219,11 @@ namespace ICSharpCode.Decompiler.Metadata @@ -151,22 +219,11 @@ namespace ICSharpCode.Decompiler.Metadata
return false;
}
return true;
case 0x1F: // ELEMENT_TYPE_CMOD_REQD
case 0x20: // ELEMENT_TYPE_CMOD_OPT
// modifier
if (!TypeHandleEquals(ref a, ref b, contextForA, contextForB))
return false;
// unmodified type
if (!IsSameCompressedInteger(ref a, ref b, out typeCode))
return false;
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode))
return false;
return true;
case 0x15: // ELEMENT_TYPE_GENERICINST
case 0x15: // ELEMENT_TYPE_GENERICINST
// generic type
if (!IsSameCompressedInteger(ref a, ref b, out typeCode))
return false;
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode))
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode, skipModifiers))
return false;
if (!IsSameCompressedInteger(ref a, ref b, out int numOfArguments))
return false;
@ -174,12 +231,12 @@ namespace ICSharpCode.Decompiler.Metadata @@ -174,12 +231,12 @@ namespace ICSharpCode.Decompiler.Metadata
{
if (!IsSameCompressedInteger(ref a, ref b, out typeCode))
return false;
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode))
if (!TypesAreEqual(ref a, ref b, contextForA, contextForB, typeCode, skipModifiers))
return false;
}
return true;
case 0x13: // ELEMENT_TYPE_VAR
case 0x1E: // ELEMENT_TYPE_MVAR
case 0x1E: // ELEMENT_TYPE_MVAR
// index
if (!IsSameCompressedInteger(ref a, ref b, out _))
return false;
@ -200,6 +257,8 @@ namespace ICSharpCode.Decompiler.Metadata @@ -200,6 +257,8 @@ namespace ICSharpCode.Decompiler.Metadata
var typeB = b.ReadTypeHandle();
if (typeA.IsNil || typeB.IsNil)
return false;
if (contextForA == contextForB)
return typeA.Equals(typeB);
return typeA.GetFullTypeName(contextForA) == typeB.GetFullTypeName(contextForB);
}
}

Loading…
Cancel
Save