Skip to content
This repository has been archived by the owner on Sep 24, 2020. It is now read-only.

Commit

Permalink
Merge NRefactory changes from SharpDevelop repository:
Browse files Browse the repository at this point in the history
- Add CecilLoader.OnEntityLoaded callback
- Fixed thread-safety of lazy-loaded cecil type system.
- Add AstNodeCollection.AcceptVisitor method
  • Loading branch information
dgrunwald committed Jul 24, 2012
1 parent 23d282d commit 7db1cbe
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 15 deletions.
16 changes: 16 additions & 0 deletions ICSharpCode.NRefactory.CSharp/Ast/AstNodeCollection.cs
Expand Up @@ -204,5 +204,21 @@ public void InsertBefore(T existingItem, T newItem)
{
node.InsertChildBefore(existingItem, newItem, role);
}

/// <summary>
/// Applies the <paramref name="visitor"/> to all nodes in this collection.
/// </summary>
public void AcceptVisitor(IAstVisitor visitor)
{
AstNode next;
for (AstNode cur = node.FirstChild; cur != null; cur = next) {
Debug.Assert(cur.Parent == node);
// Remember next before yielding cur.
// This allows removing/replacing nodes while iterating through the list.
next = cur.NextSibling;
if (cur.Role == role)
cur.AcceptVisitor(visitor);
}
}
}
}
70 changes: 55 additions & 15 deletions ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs
Expand Up @@ -55,6 +55,12 @@ public class CecilLoader
/// 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>
Expand All @@ -72,6 +78,17 @@ public class CecilLoader
/// </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>
Expand All @@ -84,19 +101,26 @@ public class CecilLoader
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>
Expand All @@ -108,6 +132,7 @@ private CecilLoader(CecilLoader loader)
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
Expand Down Expand Up @@ -169,12 +194,15 @@ public IUnresolvedAssembly LoadAssembly(AssemblyDefinition assemblyDefinition)
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
}
}
}
Expand All @@ -184,7 +212,7 @@ public IUnresolvedAssembly LoadAssembly(AssemblyDefinition assemblyDefinition)
InitTypeDefinition(cecilTypeDefs[i], typeDefs[i]);
}

RegisterCecilObject(this.currentAssembly, assemblyDefinition);
AddToTypeSystemTranslationTable(this.currentAssembly, assemblyDefinition);

var result = this.currentAssembly;
this.currentAssembly = null;
Expand Down Expand Up @@ -249,9 +277,7 @@ public IUnresolvedAssembly LoadAssemblyFile(string fileName)
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
Expand Down Expand Up @@ -1742,7 +1768,6 @@ public LazyCecilTypeDefinition(CecilLoader loader, TypeDefinition typeDefinition
this.ApplyInterningProvider(loader.InterningProvider);
}
this.Freeze();
loader.RegisterCecilObject(this, typeDefinition);
}

public override string Namespace {
Expand Down Expand Up @@ -1772,7 +1797,8 @@ public LazyCecilTypeDefinition(CecilLoader loader, TypeDefinition typeDefinition
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));
Expand All @@ -1785,7 +1811,10 @@ public LazyCecilTypeDefinition(CecilLoader loader, TypeDefinition typeDefinition
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));
Expand All @@ -1798,7 +1827,10 @@ public LazyCecilTypeDefinition(CecilLoader loader, TypeDefinition typeDefinition
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));
Expand Down Expand Up @@ -2160,7 +2192,7 @@ public IUnresolvedEvent ReadEvent(EventDefinition ev, IUnresolvedTypeDefinition
}
#endregion

void FinishReadMember(AbstractUnresolvedMember member, object cecilDefinition)
void FinishReadMember(AbstractUnresolvedMember member, MemberReference cecilDefinition)
{
if (this.InterningProvider != null)
member.ApplyInterningProvider(this.InterningProvider);
Expand All @@ -2171,7 +2203,15 @@ void FinishReadMember(AbstractUnresolvedMember member, object cecilDefinition)
#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
Expand Down

0 comments on commit 7db1cbe

Please sign in to comment.