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
public Dictionary<IUnresolvedEntity, uint> MetadataTokens = new Dictionary<IUnresolvedEntity, uint>(); public Dictionary<IUnresolvedEntity, uint> MetadataTokens = new Dictionary<IUnresolvedEntity, uint>();
public Dictionary<uint, IUnresolvedMethod> TokenToMethod = new Dictionary<uint, IUnresolvedMethod>(); public Dictionary<uint, IUnresolvedMethod> TokenToMethod = new Dictionary<uint, IUnresolvedMethod>();
public Dictionary<IUnresolvedMember, ITypeReference[]> LocalVariableTypes = new Dictionary<IUnresolvedMember, ITypeReference[]>(); 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; 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 = cecilObject.MetadataToken.ToUInt32();
uint token = cecilMethod.MetadataToken.ToUInt32(); this.MetadataTokens[entity] = token;
this.MetadataTokens[method] = token;
this.TokenToMethod[token] = method; var cecilMethod = cecilObject as Mono.Cecil.MethodDefinition;
if (cecilMethod.HasBody) if (cecilMethod != null) {
this.LocalVariableTypes[method] = cecilMethod.Body.Variables.Select(v => loader.ReadTypeReference(v.VariableType)).ToArray(); 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
return Task.Run(() => LoadModule(module, name)); 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.IncludeInternalMembers = true;
loader.LazyLoad = true; loader.LazyLoad = true;
var asm = loader.LoadAssemblyFile(name); loader.OnEntityLoaded = moduleMetadataInfo.AddMember;
var moduleMetadataInfo = new ModuleMetadataInfo(module);
foreach (var typeDef in asm.GetAllTypeDefinitions()) { var asm = loader.LoadAssembly(cecilModule.Assembly);
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);
}
}
weakTable.Add(asm, moduleMetadataInfo); weakTable.Add(asm, moduleMetadataInfo);
return asm; return asm;
} }
@ -441,8 +461,18 @@ namespace Debugger
{ {
Module module = compilation.GetAppDomain().Process.GetModule(corFunction.GetModule()); Module module = compilation.GetAppDomain().Process.GetModule(corFunction.GetModule());
var info = GetInfo(module.Assembly); var info = GetInfo(module.Assembly);
var unresolved = info.TokenToMethod[corFunction.GetToken()]; uint functionToken = corFunction.GetToken();
return unresolved.Resolve(new SimpleTypeResolveContext(module.Assembly)); 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
/// the Cecil objects to stay in memory (which can significantly increase memory usage). /// the Cecil objects to stay in memory (which can significantly increase memory usage).
/// It also prevents serialization of the Cecil-loaded type system. /// It also prevents serialization of the Cecil-loaded type system.
/// </summary> /// </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; } public bool LazyLoad { get; set; }
/// <summary> /// <summary>
@ -72,6 +78,17 @@ namespace ICSharpCode.NRefactory.TypeSystem
/// </summary> /// </summary>
public CancellationToken CancellationToken { get; set; } 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> /// <summary>
/// Gets a value indicating whether this instance stores references to the cecil objects. /// Gets a value indicating whether this instance stores references to the cecil objects.
/// </summary> /// </summary>
@ -84,19 +101,26 @@ namespace ICSharpCode.NRefactory.TypeSystem
ModuleDefinition currentModule; ModuleDefinition currentModule;
CecilUnresolvedAssembly currentAssembly; 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> /// <summary>
/// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.CecilLoader"/> class. /// Initializes a new instance of the <see cref="ICSharpCode.NRefactory.TypeSystem.CecilLoader"/> class.
/// </summary> /// </summary>
/// <param name='createCecilReferences'> /// <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. /// If true references to the cecil objects are hold. In this case the cecil loader can do a type system -> cecil mapping.
/// </param> /// </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) if (createCecilReferences)
typeSystemTranslationTable = new Dictionary<object, object> (); typeSystemTranslationTable = new Dictionary<object, object> ();
// Enable interning by default.
this.InterningProvider = new SimpleInterningProvider();
} }
/// <summary> /// <summary>
@ -108,6 +132,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
this.typeSystemTranslationTable = loader.typeSystemTranslationTable; this.typeSystemTranslationTable = loader.typeSystemTranslationTable;
this.IncludeInternalMembers = loader.IncludeInternalMembers; this.IncludeInternalMembers = loader.IncludeInternalMembers;
this.LazyLoad = loader.LazyLoad; this.LazyLoad = loader.LazyLoad;
this.OnEntityLoaded = loader.OnEntityLoaded;
this.currentModule = loader.currentModule; this.currentModule = loader.currentModule;
this.currentAssembly = loader.currentAssembly; this.currentAssembly = loader.currentAssembly;
// don't use interning - the interning provider is most likely not thread-safe // don't use interning - the interning provider is most likely not thread-safe
@ -169,12 +194,15 @@ namespace ICSharpCode.NRefactory.TypeSystem
continue; continue;
if (this.LazyLoad) { if (this.LazyLoad) {
currentAssembly.AddTypeDefinition(new LazyCecilTypeDefinition(cecilLoaderCloneForLazyLoading, td)); var t = new LazyCecilTypeDefinition(cecilLoaderCloneForLazyLoading, td);
currentAssembly.AddTypeDefinition(t);
RegisterCecilObject(t, td);
} else { } else {
var t = CreateTopLevelTypeDefinition(td); var t = CreateTopLevelTypeDefinition(td);
cecilTypeDefs.Add(td); cecilTypeDefs.Add(td);
typeDefs.Add(t); typeDefs.Add(t);
currentAssembly.AddTypeDefinition(t); currentAssembly.AddTypeDefinition(t);
// The registration will happen after the members are initialized
} }
} }
} }
@ -184,7 +212,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
InitTypeDefinition(cecilTypeDefs[i], typeDefs[i]); InitTypeDefinition(cecilTypeDefs[i], typeDefs[i]);
} }
RegisterCecilObject(this.currentAssembly, assemblyDefinition); AddToTypeSystemTranslationTable(this.currentAssembly, assemblyDefinition);
var result = this.currentAssembly; var result = this.currentAssembly;
this.currentAssembly = null; this.currentAssembly = null;
@ -249,9 +277,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
throw new ArgumentNullException("fileName"); throw new ArgumentNullException("fileName");
var param = new ReaderParameters { AssemblyResolver = new DummyAssemblyResolver() }; var param = new ReaderParameters { AssemblyResolver = new DummyAssemblyResolver() };
AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(fileName, param); AssemblyDefinition asm = AssemblyDefinition.ReadAssembly(fileName, param);
var result = LoadAssembly(asm); return LoadAssembly(asm);
RegisterCecilObject(result, asm);
return result;
} }
// used to prevent Cecil from loading referenced assemblies // used to prevent Cecil from loading referenced assemblies
@ -1742,7 +1768,6 @@ namespace ICSharpCode.NRefactory.TypeSystem
this.ApplyInterningProvider(loader.InterningProvider); this.ApplyInterningProvider(loader.InterningProvider);
} }
this.Freeze(); this.Freeze();
loader.RegisterCecilObject(this, typeDefinition);
} }
public override string Namespace { public override string Namespace {
@ -1772,7 +1797,8 @@ namespace ICSharpCode.NRefactory.TypeSystem
var result = LazyInit.VolatileRead(ref this.baseTypes); var result = LazyInit.VolatileRead(ref this.baseTypes);
if (result != null) { if (result != null) {
return result; return result;
} else { }
lock (loader.currentModule) {
result = new List<ITypeReference>(); result = new List<ITypeReference>();
loader.InitBaseTypes(cecilTypeDef, result); loader.InitBaseTypes(cecilTypeDef, result);
return LazyInit.GetOrSet(ref this.baseTypes, FreezableHelper.FreezeList(result)); return LazyInit.GetOrSet(ref this.baseTypes, FreezableHelper.FreezeList(result));
@ -1785,7 +1811,10 @@ namespace ICSharpCode.NRefactory.TypeSystem
var result = LazyInit.VolatileRead(ref this.nestedTypes); var result = LazyInit.VolatileRead(ref this.nestedTypes);
if (result != null) { if (result != null) {
return result; return result;
} else { }
lock (loader.currentModule) {
if (this.nestedTypes != null)
return this.nestedTypes;
result = new List<IUnresolvedTypeDefinition>(); result = new List<IUnresolvedTypeDefinition>();
loader.InitNestedTypes(cecilTypeDef, this, result); loader.InitNestedTypes(cecilTypeDef, this, result);
return LazyInit.GetOrSet(ref this.nestedTypes, FreezableHelper.FreezeList(result)); return LazyInit.GetOrSet(ref this.nestedTypes, FreezableHelper.FreezeList(result));
@ -1798,7 +1827,10 @@ namespace ICSharpCode.NRefactory.TypeSystem
var result = LazyInit.VolatileRead(ref this.members); var result = LazyInit.VolatileRead(ref this.members);
if (result != null) { if (result != null) {
return result; return result;
} else { }
lock (loader.currentModule) {
if (this.members != null)
return this.members;
result = new List<IUnresolvedMember>(); result = new List<IUnresolvedMember>();
loader.InitMembers(cecilTypeDef, this, result); loader.InitMembers(cecilTypeDef, this, result);
return LazyInit.GetOrSet(ref this.members, FreezableHelper.FreezeList(result)); return LazyInit.GetOrSet(ref this.members, FreezableHelper.FreezeList(result));
@ -2160,7 +2192,7 @@ namespace ICSharpCode.NRefactory.TypeSystem
} }
#endregion #endregion
void FinishReadMember(AbstractUnresolvedMember member, object cecilDefinition) void FinishReadMember(AbstractUnresolvedMember member, MemberReference cecilDefinition)
{ {
if (this.InterningProvider != null) if (this.InterningProvider != null)
member.ApplyInterningProvider(this.InterningProvider); member.ApplyInterningProvider(this.InterningProvider);
@ -2171,7 +2203,15 @@ namespace ICSharpCode.NRefactory.TypeSystem
#region Type system translation table #region Type system translation table
readonly Dictionary<object, object> typeSystemTranslationTable; 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) { if (typeSystemTranslationTable != null) {
// When lazy-loading, the dictionary might be shared between multiple cecil-loaders that are used concurrently // When lazy-loading, the dictionary might be shared between multiple cecil-loaders that are used concurrently

Loading…
Cancel
Save