Browse Source

Lazy-load the debugger type system.

newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
c924cd6aef
  1. 110
      src/AddIns/Debugger/Debugger.Core/TypeSystemExtensions.cs
  2. 70
      src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs

110
src/AddIns/Debugger/Debugger.Core/TypeSystemExtensions.cs

@ -30,20 +30,61 @@ namespace Debugger @@ -30,20 +30,61 @@ namespace Debugger
public Dictionary<IUnresolvedEntity, uint> MetadataTokens = new Dictionary<IUnresolvedEntity, uint>();
public Dictionary<uint, IUnresolvedMethod> TokenToMethod = new Dictionary<uint, IUnresolvedMethod>();
public Dictionary<IUnresolvedMember, ITypeReference[]> LocalVariableTypes = new Dictionary<IUnresolvedMember, ITypeReference[]>();
readonly CecilLoader typeRefLoader;
public ModuleMetadataInfo(Module module)
public ModuleMetadataInfo(Module module, Mono.Cecil.ModuleDefinition cecilModule)
{
this.Module = module;
typeRefLoader = new CecilLoader();
typeRefLoader.SetCurrentModule(cecilModule);
}
public void AddMethod(IUnresolvedMethod method, CecilLoader loader)
public void AddMember(IUnresolvedEntity entity, Mono.Cecil.MemberReference cecilObject)
{
var cecilMethod = loader.GetCecilObject(method);
uint token = cecilMethod.MetadataToken.ToUInt32();
this.MetadataTokens[method] = token;
this.TokenToMethod[token] = method;
if (cecilMethod.HasBody)
this.LocalVariableTypes[method] = cecilMethod.Body.Variables.Select(v => loader.ReadTypeReference(v.VariableType)).ToArray();
uint token = cecilObject.MetadataToken.ToUInt32();
this.MetadataTokens[entity] = token;
var cecilMethod = cecilObject as Mono.Cecil.MethodDefinition;
if (cecilMethod != null) {
IUnresolvedMethod method = (IUnresolvedMethod)entity;
this.TokenToMethod[token] = method;
if (cecilMethod.HasBody) {
var locals = cecilMethod.Body.Variables;
if (locals.Count > 0) {
this.LocalVariableTypes[method] = locals.Select(v => typeRefLoader.ReadTypeReference(v.VariableType)).ToArray();
}
if (cecilMethod.RVA != 0) {
// The method was loaded from image - we can free the memory for the body
// because Cecil will re-initialize it on demand
cecilMethod.Body = null;
}
}
}
}
}
// used to prevent Cecil from loading referenced assemblies
sealed class DummyAssemblyResolver : Mono.Cecil.IAssemblyResolver
{
public Mono.Cecil.AssemblyDefinition Resolve(Mono.Cecil.AssemblyNameReference name)
{
return null;
}
public Mono.Cecil.AssemblyDefinition Resolve(string fullName)
{
return null;
}
public Mono.Cecil.AssemblyDefinition Resolve(Mono.Cecil.AssemblyNameReference name, Mono.Cecil.ReaderParameters parameters)
{
return null;
}
public Mono.Cecil.AssemblyDefinition Resolve(string fullName, Mono.Cecil.ReaderParameters parameters)
{
return null;
}
}
@ -57,39 +98,18 @@ namespace Debugger @@ -57,39 +98,18 @@ namespace Debugger
return Task.Run(() => LoadModule(module, name));
}
static IUnresolvedAssembly LoadModule(Module module, string name)
static IUnresolvedAssembly LoadModule(Module module, string fileName)
{
CecilLoader loader = new CecilLoader(true);
var param = new Mono.Cecil.ReaderParameters { AssemblyResolver = new DummyAssemblyResolver() };
var cecilModule = Mono.Cecil.ModuleDefinition.ReadModule(fileName, param);
var moduleMetadataInfo = new ModuleMetadataInfo(module, cecilModule);
var loader = new CecilLoader();
loader.IncludeInternalMembers = true;
loader.LazyLoad = true;
var asm = loader.LoadAssemblyFile(name);
var moduleMetadataInfo = new ModuleMetadataInfo(module);
foreach (var typeDef in asm.GetAllTypeDefinitions()) {
var cecilTypeDef = loader.GetCecilObject(typeDef);
loader.SetCurrentModule(cecilTypeDef.Module);
moduleMetadataInfo.MetadataTokens[typeDef] = cecilTypeDef.MetadataToken.ToUInt32();
foreach (var member in typeDef.Fields) {
var cecilMember = loader.GetCecilObject(member);
moduleMetadataInfo.MetadataTokens[member] = cecilMember.MetadataToken.ToUInt32();
}
foreach (var member in typeDef.Methods) {
moduleMetadataInfo.AddMethod(member, loader);
}
foreach (var member in typeDef.Properties) {
if (member.CanGet)
moduleMetadataInfo.AddMethod(member.Getter, loader);
if (member.CanSet)
moduleMetadataInfo.AddMethod(member.Setter, loader);
}
foreach (var member in typeDef.Events) {
if (member.CanAdd)
moduleMetadataInfo.AddMethod(member.AddAccessor, loader);
if (member.CanRemove)
moduleMetadataInfo.AddMethod(member.RemoveAccessor, loader);
if (member.CanInvoke)
moduleMetadataInfo.AddMethod(member.InvokeAccessor, loader);
}
}
loader.OnEntityLoaded = moduleMetadataInfo.AddMember;
var asm = loader.LoadAssembly(cecilModule.Assembly);
weakTable.Add(asm, moduleMetadataInfo);
return asm;
}
@ -441,8 +461,18 @@ namespace Debugger @@ -441,8 +461,18 @@ namespace Debugger
{
Module module = compilation.GetAppDomain().Process.GetModule(corFunction.GetModule());
var info = GetInfo(module.Assembly);
var unresolved = info.TokenToMethod[corFunction.GetToken()];
return unresolved.Resolve(new SimpleTypeResolveContext(module.Assembly));
uint functionToken = corFunction.GetToken();
IUnresolvedMethod unresolvedMethod;
if (!info.TokenToMethod.TryGetValue(functionToken, out unresolvedMethod)) {
// The type containing this function wasn't loaded yet
uint classToken = corFunction.GetClass().GetToken();
var definition = ToTypeDefinitionReference(module, classToken).Resolve(new SimpleTypeResolveContext(module.Assembly)).GetDefinition();
if (definition == null)
throw new InvalidOperationException("Could not find class for token " + classToken);
definition.Methods.ToList(); // enforce loading the methods so that they get added to the dictionary
unresolvedMethod = info.TokenToMethod[functionToken];
}
return unresolvedMethod.Resolve(new SimpleTypeResolveContext(module.Assembly));
}
}
}

70
src/Libraries/NRefactory/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs

@ -55,6 +55,12 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -55,6 +55,12 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// the Cecil objects to stay in memory (which can significantly increase memory usage).
/// It also prevents serialization of the Cecil-loaded type system.
/// </summary>
/// <remarks>
/// Because the type system can be used on multiple threads, but Cecil is not
/// thread-safe for concurrent read access, the CecilLoader will lock on the <see cref="ModuleDefinition"/> instance
/// for every delay-loading operation.
/// If you access the Cecil objects directly in your application, you may need to take the same lock.
/// </remarks>
public bool LazyLoad { get; set; }
/// <summary>
@ -72,6 +78,17 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -72,6 +78,17 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary>
public CancellationToken CancellationToken { get; set; }
/// <summary>
/// This delegate gets executed whenever an entity was loaded.
/// </summary>
/// <remarks>
/// This callback may be to build a dictionary that maps between
/// entities and cecil objects.
/// Warning: if delay-loading is used and the type system is accessed by multiple threads,
/// the callback may be invoked concurrently on multiple threads.
/// </remarks>
public Action<IUnresolvedEntity, MemberReference> OnEntityLoaded { get; set; }
/// <summary>
/// Gets a value indicating whether this instance stores references to the cecil objects.
/// </summary>
@ -84,19 +101,26 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -84,19 +101,26 @@ namespace ICSharpCode.NRefactory.TypeSystem
ModuleDefinition currentModule;
CecilUnresolvedAssembly currentAssembly;
/// <summary>
/// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.CecilLoader"/> class.
/// </summary>
public CecilLoader()
{
// Enable interning by default.
this.InterningProvider = new SimpleInterningProvider();
}
/// <summary>
/// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.CecilLoader"/> class.
/// </summary>
/// <param name='createCecilReferences'>
/// If true references to the cecil objects are hold. In this case the cecil loader can do a type system -> cecil mapping.
/// </param>
public CecilLoader (bool createCecilReferences = false)
[Obsolete("The built-in entity<->cecil mapping is obsolete. Use the OnEntityLoaded callback instead!")]
public CecilLoader(bool createCecilReferences) : this()
{
if (createCecilReferences)
typeSystemTranslationTable = new Dictionary<object, object> ();
// Enable interning by default.
this.InterningProvider = new SimpleInterningProvider();
}
/// <summary>
@ -108,6 +132,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -108,6 +132,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
this.typeSystemTranslationTable = loader.typeSystemTranslationTable;
this.IncludeInternalMembers = loader.IncludeInternalMembers;
this.LazyLoad = loader.LazyLoad;
this.OnEntityLoaded = loader.OnEntityLoaded;
this.currentModule = loader.currentModule;
this.currentAssembly = loader.currentAssembly;
// don't use interning - the interning provider is most likely not thread-safe
@ -169,12 +194,15 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -169,12 +194,15 @@ namespace ICSharpCode.NRefactory.TypeSystem
continue;
if (this.LazyLoad) {
currentAssembly.AddTypeDefinition(new LazyCecilTypeDefinition(cecilLoaderCloneForLazyLoading, td));
var t = new LazyCecilTypeDefinition(cecilLoaderCloneForLazyLoading, td);
currentAssembly.AddTypeDefinition(t);
RegisterCecilObject(t, td);
} else {
var t = CreateTopLevelTypeDefinition(td);
cecilTypeDefs.Add(td);
typeDefs.Add(t);
currentAssembly.AddTypeDefinition(t);
// The registration will happen after the members are initialized
}
}
}
@ -184,7 +212,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -184,7 +212,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
InitTypeDefinition(cecilTypeDefs[i], typeDefs[i]);
}
RegisterCecilObject(this.currentAssembly, assemblyDefinition);
AddToTypeSystemTranslationTable(this.currentAssembly, assemblyDefinition);
var result = this.currentAssembly;
this.currentAssembly = null;
@ -249,9 +277,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -249,9 +277,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
throw new ArgumentNullException("fileName");
var param = new ReaderParameters { AssemblyResolver = new DummyAssemblyResolver() };
AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(fileName, param);
var result = LoadAssembly(asm);
RegisterCecilObject(result, asm);
return result;
return LoadAssembly(asm);
}
// used to prevent Cecil from loading referenced assemblies
@ -1742,7 +1768,6 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -1742,7 +1768,6 @@ namespace ICSharpCode.NRefactory.TypeSystem
this.ApplyInterningProvider(loader.InterningProvider);
}
this.Freeze();
loader.RegisterCecilObject(this, typeDefinition);
}
public override string Namespace {
@ -1772,7 +1797,8 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -1772,7 +1797,8 @@ namespace ICSharpCode.NRefactory.TypeSystem
var result = LazyInit.VolatileRead(ref this.baseTypes);
if (result != null) {
return result;
} else {
}
lock (loader.currentModule) {
result = new List<ITypeReference>();
loader.InitBaseTypes(cecilTypeDef, result);
return LazyInit.GetOrSet(ref this.baseTypes, FreezableHelper.FreezeList(result));
@ -1785,7 +1811,10 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -1785,7 +1811,10 @@ namespace ICSharpCode.NRefactory.TypeSystem
var result = LazyInit.VolatileRead(ref this.nestedTypes);
if (result != null) {
return result;
} else {
}
lock (loader.currentModule) {
if (this.nestedTypes != null)
return this.nestedTypes;
result = new List<IUnresolvedTypeDefinition>();
loader.InitNestedTypes(cecilTypeDef, this, result);
return LazyInit.GetOrSet(ref this.nestedTypes, FreezableHelper.FreezeList(result));
@ -1798,7 +1827,10 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -1798,7 +1827,10 @@ namespace ICSharpCode.NRefactory.TypeSystem
var result = LazyInit.VolatileRead(ref this.members);
if (result != null) {
return result;
} else {
}
lock (loader.currentModule) {
if (this.members != null)
return this.members;
result = new List<IUnresolvedMember>();
loader.InitMembers(cecilTypeDef, this, result);
return LazyInit.GetOrSet(ref this.members, FreezableHelper.FreezeList(result));
@ -2160,7 +2192,7 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -2160,7 +2192,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
}
#endregion
void FinishReadMember(AbstractUnresolvedMember member, object cecilDefinition)
void FinishReadMember(AbstractUnresolvedMember member, MemberReference cecilDefinition)
{
if (this.InterningProvider != null)
member.ApplyInterningProvider(this.InterningProvider);
@ -2171,7 +2203,15 @@ namespace ICSharpCode.NRefactory.TypeSystem @@ -2171,7 +2203,15 @@ namespace ICSharpCode.NRefactory.TypeSystem
#region Type system translation table
readonly Dictionary<object, object> typeSystemTranslationTable;
void RegisterCecilObject(object typeSystemObject, object cecilObject)
void RegisterCecilObject(IUnresolvedEntity typeSystemObject, MemberReference cecilObject)
{
if (OnEntityLoaded != null)
OnEntityLoaded(typeSystemObject, cecilObject);
AddToTypeSystemTranslationTable(typeSystemObject, cecilObject);
}
void AddToTypeSystemTranslationTable(object typeSystemObject, object cecilObject)
{
if (typeSystemTranslationTable != null) {
// When lazy-loading, the dictionary might be shared between multiple cecil-loaders that are used concurrently

Loading…
Cancel
Save