/*
Copyright (C) 2014-2019 de4dot@gmail.com
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see .
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using dnlib.DotNet;
namespace dnSpy.AsmEditor.Compiler {
sealed partial class ModuleImporter {
struct MemberLookup {
readonly Dictionary properties;
readonly Dictionary events;
readonly Dictionary methods;
readonly Dictionary fields;
readonly Dictionary methodOverrides;
/*readonly*/ ImportSigComparer comparer;
TypeDef targetType;
struct MethodAndOverride {
public MethodDef TargetMethod { get; }
public IMethodDefOrRef MethodDeclaration { get; }
public MethodAndOverride(MethodDef targetMethod, IMethodDefOrRef methodDeclaration) {
TargetMethod = targetMethod;
MethodDeclaration = methodDeclaration;
}
}
public MemberLookup(ImportSigComparer comparer) {
properties = new Dictionary(new ImportPropertyEqualityComparer(comparer));
events = new Dictionary(new ImportEventEqualityComparer(comparer));
methods = new Dictionary(new ImportMethodEqualityComparer(comparer));
fields = new Dictionary(new ImportFieldEqualityComparer(comparer));
methodOverrides = new Dictionary(new ImportMethodEqualityComparer(comparer));
this.comparer = comparer;
targetType = null!;
}
public void Initialize(TypeDef targetType) {
this.targetType = targetType;
properties.Clear();
events.Clear();
methods.Clear();
fields.Clear();
foreach (var p in targetType.Properties)
properties[p] = p;
foreach (var e in targetType.Events)
events[e] = e;
foreach (var m in targetType.Methods) {
methods[m] = m;
foreach (var o in m.Overrides)
methodOverrides[o.MethodDeclaration] = new MethodAndOverride(m, o.MethodDeclaration);
}
foreach (var f in targetType.Fields)
fields[f] = f;
}
MethodDef? LookupOverride(MethodOverride o) {
if (!methodOverrides.TryGetValue(o.MethodDeclaration, out var info))
return null;
if (!comparer.Equals(info.MethodDeclaration.DeclaringType, o.MethodDeclaration.DeclaringType))
return null;
return info.TargetMethod;
}
public PropertyDef? FindProperty(PropertyDef compiledProp) {
if (properties.TryGetValue(compiledProp, out var targetProp))
return targetProp;
return FindPropertyOverride(compiledProp.GetMethod) ?? FindPropertyOverride(compiledProp.SetMethod);
}
PropertyDef? FindPropertyOverride(MethodDef compiledMethod) {
if (compiledMethod is null)
return null;
foreach (var o in compiledMethod.Overrides) {
var targetMethod = LookupOverride(o);
if (targetMethod is not null)
return targetMethod.DeclaringType.Properties.First(a => a.GetMethod == targetMethod || a.SetMethod == targetMethod);
}
return null;
}
public EventDef? FindEvent(EventDef compiledEvent) {
if (events.TryGetValue(compiledEvent, out var targetEvent))
return targetEvent;
return FindEventOverride(compiledEvent.AddMethod) ?? FindEventOverride(compiledEvent.RemoveMethod) ?? FindEventOverride(compiledEvent.InvokeMethod);
}
EventDef? FindEventOverride(MethodDef compiledMethod) {
if (compiledMethod is null)
return null;
foreach (var o in compiledMethod.Overrides) {
var targetMethod = LookupOverride(o);
if (targetMethod is not null)
return targetMethod.DeclaringType.Events.First(a => a.AddMethod == targetMethod || a.RemoveMethod == targetMethod || a.InvokeMethod == targetMethod);
}
return null;
}
public MethodDef? FindMethod(MethodDef compiledMethod) {
MethodDef? targetMethod;
if (methods.TryGetValue(compiledMethod, out targetMethod))
return targetMethod;
foreach (var o in compiledMethod.Overrides) {
targetMethod = LookupOverride(o);
if (targetMethod is not null)
return targetMethod;
}
return null;
}
public FieldDef? FindField(FieldDef compiledField) {
fields.TryGetValue(compiledField, out var targetField);
return targetField;
}
public void Remove(PropertyDef targetProp) {
if (targetProp.Module != targetType.Module)
throw new InvalidOperationException();
bool b = properties.Remove(targetProp);
Debug.Assert(b);
}
public void Remove(EventDef targetEvent) {
if (targetEvent.Module != targetType.Module)
throw new InvalidOperationException();
bool b = events.Remove(targetEvent);
Debug.Assert(b);
}
public void Remove(MethodDef targetMethod) {
if (targetMethod.Module != targetType.Module)
throw new InvalidOperationException();
bool b = methods.Remove(targetMethod);
Debug.Assert(b);
}
public void Remove(FieldDef targetField) {
if (targetField.Module != targetType.Module)
throw new InvalidOperationException();
bool b = fields.Remove(targetField);
Debug.Assert(b);
}
}
}
}