Browse Source

Fix the ordering of COM interface methods and properties to appear in their originally defined order. This was resulting in fatal crashes while running decompiled COM interop code.

pull/2639/head
Zachary Northrup 3 years ago committed by Siegfried Pammer
parent
commit
9c17029702
  1. 114
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

114
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -429,6 +429,47 @@ namespace ICSharpCode.Decompiler.CSharp @@ -429,6 +429,47 @@ namespace ICSharpCode.Decompiler.CSharp
}
#endregion
#region NativeOrdering
/// <summary>
/// Determines whether a given type requires that its methods be ordered precisely as they were originally defined.
/// </summary>
/// <param name="typeDef">The type whose members may need native ordering.</param>
internal bool RequiresNativeOrdering(ITypeDefinition typeDef)
{
// The main scenario for requiring the native method ordering is COM interop, where the V-table is fixed by the ABI
return ComHelper.IsComImport(typeDef);
}
/// <summary>
/// Compare handles with the method definition ordering intact by using the underlying method's MetadataToken,
/// which is defined as the index into a given metadata table. This should equate to the original order that
/// methods and properties were defined by the author.
/// </summary>
/// <param name="typeDef">The type whose members to order using their method's MetadataToken</param>
/// <returns>A sequence of all members ordered by MetadataToken</returns>
internal IEnumerable<IMember> GetMembersWithNativeOrdering(ITypeDefinition typeDef)
{
EntityHandle GetOrderingHandle(IMember member)
{
// Note! Technically COM interfaces could define property getters and setters out of order or interleaved with other
// methods, but C# doesn't support this so we can't define it that way.
if (member is IMethod)
return member.MetadataToken;
else if (member is IProperty property)
return property.Getter?.MetadataToken ?? property.Setter?.MetadataToken ?? property.MetadataToken;
else if (member is IEvent @event)
return @event.AddAccessor?.MetadataToken ?? @event.RemoveAccessor?.MetadataToken ?? @event.InvokeAccessor?.MetadataToken ?? @event.MetadataToken;
else
return member.MetadataToken;
}
return typeDef.Fields.Concat<IMember>(typeDef.Properties).Concat(typeDef.Methods).Concat(typeDef.Events).OrderBy((member) => GetOrderingHandle(member), HandleComparer.Default);
}
#endregion
static PEFile LoadPEFile(string fileName, DecompilerSettings settings)
{
settings.LoadInMemory = true;
@ -1264,48 +1305,61 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1264,48 +1305,61 @@ namespace ICSharpCode.Decompiler.CSharp
// With C# 9 records, the relative order of fields and properties matters:
IEnumerable<IMember> fieldsAndProperties = recordDecompiler?.FieldsAndProperties
?? typeDef.Fields.Concat<IMember>(typeDef.Properties);
foreach (var fieldOrProperty in fieldsAndProperties)
// For COM interop scenarios, the relative order of virtual functions/properties matters:
IEnumerable<IMember> allOrderedMembers = RequiresNativeOrdering(typeDef) ? GetMembersWithNativeOrdering(typeDef) :
fieldsAndProperties.Concat<IMember>(typeDef.Events).Concat<IMember>(typeDef.Methods);
foreach (var member in allOrderedMembers)
{
if (fieldOrProperty.MetadataToken.IsNil || MemberIsHidden(module.PEFile, fieldOrProperty.MetadataToken, settings))
{
continue;
}
if (fieldOrProperty is IField field)
if (member is IField || member is IProperty)
{
if (typeDef.Kind == TypeKind.Enum && !field.IsConst)
var fieldOrProperty = member;
if (fieldOrProperty.MetadataToken.IsNil || MemberIsHidden(module.PEFile, fieldOrProperty.MetadataToken, settings))
{
continue;
var memberDecl = DoDecompile(field, decompileRun, decompilationContext.WithCurrentMember(field));
typeDecl.Members.Add(memberDecl);
}
if (fieldOrProperty is IField field)
{
if (typeDef.Kind == TypeKind.Enum && !field.IsConst)
continue;
var memberDecl = DoDecompile(field, decompileRun, decompilationContext.WithCurrentMember(field));
typeDecl.Members.Add(memberDecl);
}
else if (fieldOrProperty is IProperty property)
{
if (recordDecompiler?.PropertyIsGenerated(property) == true)
{
continue;
}
var propDecl = DoDecompile(property, decompileRun, decompilationContext.WithCurrentMember(property));
typeDecl.Members.Add(propDecl);
}
}
else if (fieldOrProperty is IProperty property)
else if (member is IMethod method)
{
if (recordDecompiler?.PropertyIsGenerated(property) == true)
if (recordDecompiler?.MethodIsGenerated(method) == true)
{
continue;
}
var propDecl = DoDecompile(property, decompileRun, decompilationContext.WithCurrentMember(property));
typeDecl.Members.Add(propDecl);
}
}
foreach (var @event in typeDef.Events)
{
if (!@event.MetadataToken.IsNil && !MemberIsHidden(module.PEFile, @event.MetadataToken, settings))
{
var eventDecl = DoDecompile(@event, decompileRun, decompilationContext.WithCurrentMember(@event));
typeDecl.Members.Add(eventDecl);
if (!method.MetadataToken.IsNil && !MemberIsHidden(module.PEFile, method.MetadataToken, settings))
{
var memberDecl = DoDecompile(method, decompileRun, decompilationContext.WithCurrentMember(method));
typeDecl.Members.Add(memberDecl);
typeDecl.Members.AddRange(AddInterfaceImplHelpers(memberDecl, method, typeSystemAstBuilder));
}
}
}
foreach (var method in typeDef.Methods)
{
if (recordDecompiler?.MethodIsGenerated(method) == true)
else if (member is IEvent @event)
{
continue;
if (!@event.MetadataToken.IsNil && !MemberIsHidden(module.PEFile, @event.MetadataToken, settings))
{
var eventDecl = DoDecompile(@event, decompileRun, decompilationContext.WithCurrentMember(@event));
typeDecl.Members.Add(eventDecl);
}
}
if (!method.MetadataToken.IsNil && !MemberIsHidden(module.PEFile, method.MetadataToken, settings))
else
{
var memberDecl = DoDecompile(method, decompileRun, decompilationContext.WithCurrentMember(method));
typeDecl.Members.Add(memberDecl);
typeDecl.Members.AddRange(AddInterfaceImplHelpers(memberDecl, method, typeSystemAstBuilder));
throw new ArgumentOutOfRangeException("Unexpected member type");
}
}
if (typeDecl.Members.OfType<IndexerDeclaration>().Any(idx => idx.PrivateImplementationType.IsNull))

Loading…
Cancel
Save