From 7db1cbe11e5a12bbcba82da5e36826ab185afcb2 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 23 Jul 2012 21:03:32 +0200 Subject: [PATCH] Merge NRefactory changes from SharpDevelop repository: - Add CecilLoader.OnEntityLoaded callback - Fixed thread-safety of lazy-loaded cecil type system. - Add AstNodeCollection.AcceptVisitor method --- .../Ast/AstNodeCollection.cs | 16 +++++ .../TypeSystem/CecilLoader.cs | 70 +++++++++++++++---- 2 files changed, 71 insertions(+), 15 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Ast/AstNodeCollection.cs b/ICSharpCode.NRefactory.CSharp/Ast/AstNodeCollection.cs index 4e28849be4..b727dc2112 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/AstNodeCollection.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/AstNodeCollection.cs @@ -204,5 +204,21 @@ namespace ICSharpCode.NRefactory.CSharp { node.InsertChildBefore(existingItem, newItem, role); } + + /// + /// Applies the to all nodes in this collection. + /// + 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); + } + } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs index e4322822f9..0eb735a759 100644 --- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs +++ b/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). /// It also prevents serialization of the Cecil-loaded type system. /// + /// + /// 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 instance + /// for every delay-loading operation. + /// If you access the Cecil objects directly in your application, you may need to take the same lock. + /// public bool LazyLoad { get; set; } /// @@ -72,6 +78,17 @@ namespace ICSharpCode.NRefactory.TypeSystem /// public CancellationToken CancellationToken { get; set; } + /// + /// This delegate gets executed whenever an entity was loaded. + /// + /// + /// 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. + /// + public Action OnEntityLoaded { get; set; } + /// /// Gets a value indicating whether this instance stores references to the cecil objects. /// @@ -84,19 +101,26 @@ namespace ICSharpCode.NRefactory.TypeSystem ModuleDefinition currentModule; CecilUnresolvedAssembly currentAssembly; + /// + /// Initializes a new instance of the class. + /// + public CecilLoader() + { + // Enable interning by default. + this.InterningProvider = new SimpleInterningProvider(); + } + /// /// Initializes a new instance of the class. /// /// /// If true references to the cecil objects are hold. In this case the cecil loader can do a type system -> cecil mapping. /// - 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 (); - - // Enable interning by default. - this.InterningProvider = new SimpleInterningProvider(); } /// @@ -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 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 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 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 this.ApplyInterningProvider(loader.InterningProvider); } this.Freeze(); - loader.RegisterCecilObject(this, typeDefinition); } public override string Namespace { @@ -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(); loader.InitBaseTypes(cecilTypeDef, 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); if (result != null) { return result; - } else { + } + lock (loader.currentModule) { + if (this.nestedTypes != null) + return this.nestedTypes; result = new List(); loader.InitNestedTypes(cecilTypeDef, this, 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); if (result != null) { return result; - } else { + } + lock (loader.currentModule) { + if (this.members != null) + return this.members; result = new List(); loader.InitMembers(cecilTypeDef, this, result); return LazyInit.GetOrSet(ref this.members, FreezableHelper.FreezeList(result)); @@ -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 #region Type system translation table readonly Dictionary 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